Board Game Arena tournoidoc139_fi http:///Etusivu MediaWiki 1.39.0 first-letter Media Toiminnot Keskustelu Käyttäjä Keskustelu käyttäjästä Board Game Arena Keskustelu Board Game Arenasta Tiedosto Keskustelu tiedostosta Järjestelmäviesti Keskustelu järjestelmäviestistä Malline Keskustelu mallineesta Ohje Keskustelu ohjeesta Luokka Keskustelu luokasta Main Page 0 1 1 2012-01-05T15:36:33Z MediaWiki default 0 wikitext text/x-wiki '''MediaWiki has been successfully installed.''' Consult the [//meta.wikimedia.org/wiki/Help:Contents User's Guide] for information on using the wiki software. == Getting started == * [//www.mediawiki.org/wiki/Manual:Configuration_settings Configuration settings list] * [//www.mediawiki.org/wiki/Manual:FAQ MediaWiki FAQ] * [https://lists.wikimedia.org/mailman/listinfo/mediawiki-announce MediaWiki release mailing list] b7a3846f2c55072191227d89a3204fe379288fee 6 1 2012-01-23T08:20:44Z Sourisdudesert 1 Redirected page to [[Help]] wikitext text/x-wiki #REDIRECT [[Help]] 716110d80c64acdd324d04dd290c1bffae7bce06 Järjestelmäviesti:Sidebar 8 2 2 2012-01-06T14:15:59Z Sourisdudesert 1 Created page with " * navigation ** http://boardgamearena.com|BoardGameArena.com ** mainpage|mainpage-description ** recentchanges-url|recentchanges * SEARCH * TOOLBOX * LANGUAGES" wikitext text/x-wiki * navigation ** http://boardgamearena.com|BoardGameArena.com ** mainpage|mainpage-description ** recentchanges-url|recentchanges * SEARCH * TOOLBOX * LANGUAGES bd8432e95416d227d9bca445c1f77a70aee1fbec Faq 0 3 3 2012-01-20T20:48:59Z Sourisdudesert 1 Created page with " test [[File:Reputation_down.png]]" wikitext text/x-wiki test [[File:Reputation_down.png]] 7b93a95fc5cc35ff8637c12da14176e1205f1e01 4 3 2012-01-20T21:10:48Z Sourisdudesert 1 wikitext text/x-wiki test [[File:Reputation_down.png]] http://en.wikipedia.org/upload/b/bc/Wiki.png 9195da8cff4846394bfa49361b55be1597869f51 5 4 2012-01-20T21:11:12Z Sourisdudesert 1 wikitext text/x-wiki test [[File:Reputation_down.png]] http://en.wikipedia.org/upload/b/bc/Wiki.png http://static.tournoi-en-ligne.fr/data/themereleases/120120-1440/img/stoneage/game_icon.png 92692e80e27e413417c9e34326c6e0df02a4ba1c 8 5 2012-01-23T08:25:52Z Sourisdudesert 1 wikitext text/x-wiki == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the bug section in the forum, see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. What is the legal status of this website? All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. I'm a game publisher: why would I like to have my game on Board Game Arena? The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. Meeting players and starting games I launched a new table but nobody is joining? Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. I joined a game. When does the game start? Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. What is the meaning of the small color circle next to players names? : this player is active. He completed an action very recently. : this player is inactive. He is connected to the website but did not perform any action recently. : this player is offline. During the game What is the meaning of the icons next to players names? : this player must make a move now. : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. A player has to make a move but he/she doesn't. What can I do? First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. Then you can ask this player to confirm that he is still thinking about how to play. If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. What is the current progression of the game I'm playing? A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. What is forbidden on Board Game Arena? Leaving a game in progress on purpose. Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious loosing situation. Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. Running out of time on the game clock. Giving some piece of information about the current game situation that corrupts the normal game flow. Communicating with another player about the game privately (ex: with MSN). Provocation / triumphalism / defeatism ... anything which is not fair play. Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. What is absolutely forbidden in Board Game Arena? Creating multiple account and playing against oneself. Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. What if some player does something wrong? Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. ab1d2e0d199f098d3288fe43081e422ab59e699c 9 8 2012-01-23T08:28:22Z Sourisdudesert 1 wikitext text/x-wiki == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== : this player is active. He completed an action very recently. : this player is inactive. He is connected to the website but did not perform any action recently. : this player is offline. == During the game == ===What is the meaning of the icons next to players names?=== : this player must make a move now. : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious loosing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 556e32db4b9111710ffcbc1ad05f9ec857794e31 10 9 2012-01-23T08:29:03Z Sourisdudesert 1 /* What is the meaning of the small color circle next to players names? */ wikitext text/x-wiki == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. == During the game == ===What is the meaning of the icons next to players names?=== : this player must make a move now. : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious loosing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. c6e6418ab580cd5812c4f613e1be5737fa3a6f7c 11 10 2012-01-23T08:29:43Z Sourisdudesert 1 /* What is the meaning of the icons next to players names? */ wikitext text/x-wiki == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious loosing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 3678e480ae646ee50cada05e6bf34cac1faf094c 24 11 2012-01-23T08:43:03Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious loosing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 929b9aebaa71e05b8e961016d35d724169bbcf2d Help 0 4 7 2012-01-23T08:23:35Z Sourisdudesert 1 Created page with "An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com]] Forums are also helpful to get information. == Hel..." wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com]] Forums are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailled help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] bb066b4658e936f326aa0ee996bb45e23cb8c2b0 12 7 2012-01-23T08:30:09Z Sourisdudesert 1 wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com] Forums are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailled help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] 95118ab41bd97e3433383cecebb8e7e133c40b10 13 12 2012-01-23T08:30:21Z Sourisdudesert 1 wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailled help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] 16b14d9549ac5aa2e310653b33e55be4abd27bb0 About Board Game Arena 0 5 14 2012-01-23T08:31:22Z Sourisdudesert 1 Created page with "With '''Board Game Arena''' you can play board games online with the whole world. Board Game Arena is a next generation game platform which makes online gaming more simple a..." wikitext text/x-wiki With '''Board Game Arena''' you can play board games online with the whole world. Board Game Arena is a next generation game platform which makes online gaming more simple and attractive: * Nothing to install: play straight from your browser, from anywhere, at anytime. * Real time: your opponents are online, you play "live". * Simple: if you know the game, then you know how to play online. * Free. Board Game Arena offers a [http://en.boardgamearena.com/#!gamelist growing selection of board and card games]. We are specialized in modern adults games ("European style games"). This game platform has been designed to host any kind of game. This flexibility allow us to propose a '''new game every month'''. b9d59b3d8e80f8df79151e82dbdede2ee6efc91c 25 14 2012-01-23T08:43:15Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] With '''Board Game Arena''' you can play board games online with the whole world. Board Game Arena is a next generation game platform which makes online gaming more simple and attractive: * Nothing to install: play straight from your browser, from anywhere, at anytime. * Real time: your opponents are online, you play "live". * Simple: if you know the game, then you know how to play online. * Free. Board Game Arena offers a [http://en.boardgamearena.com/#!gamelist growing selection of board and card games]. We are specialized in modern adults games ("European style games"). This game platform has been designed to host any kind of game. This flexibility allow us to propose a '''new game every month'''. 4f7854886444585b16fecb70e4b46c419e6db368 28 25 2012-01-23T08:43:48Z Sourisdudesert 1 wikitext text/x-wiki With '''Board Game Arena''' you can play board games online with the whole world. Board Game Arena is a next generation game platform which makes online gaming more simple and attractive: * Nothing to install: play straight from your browser, from anywhere, at anytime. * Real time: your opponents are online, you play "live". * Simple: if you know the game, then you know how to play online. * Free. Board Game Arena offers a [http://en.boardgamearena.com/#!gamelist growing selection of board and card games]. We are specialized in modern adults games ("European style games"). This game platform has been designed to host any kind of game. This flexibility allow us to propose a '''new game every month'''. b9d59b3d8e80f8df79151e82dbdede2ee6efc91c About us 0 6 15 2012-01-23T08:31:55Z Sourisdudesert 1 Created page with "First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic..." wikitext text/x-wiki First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic or social constraints. We would like to provide a new game experience with the best of two worlds: video games and board games. Although we aim to provide high quality online games, we are still convinced that the best way to enjoy board games is around a *real* table with friends. This is one of the reason we strongly recommand you to buy physical copies of the games you discover on BGA. The other reason is simple to understand: each game sale is also a revenue for an author and an editor (... we've come full circle). [[Contact us]] 09d076571c067045048850f642e222a1e1485eb7 26 15 2012-01-23T08:43:26Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic or social constraints. We would like to provide a new game experience with the best of two worlds: video games and board games. Although we aim to provide high quality online games, we are still convinced that the best way to enjoy board games is around a *real* table with friends. This is one of the reason we strongly recommand you to buy physical copies of the games you discover on BGA. The other reason is simple to understand: each game sale is also a revenue for an author and an editor (... we've come full circle). [[Contact us]] 366663e98e2fe4f1c5659450c36d0b7db247b03f 27 26 2012-01-23T08:43:39Z Sourisdudesert 1 wikitext text/x-wiki First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic or social constraints. We would like to provide a new game experience with the best of two worlds: video games and board games. Although we aim to provide high quality online games, we are still convinced that the best way to enjoy board games is around a *real* table with friends. This is one of the reason we strongly recommand you to buy physical copies of the games you discover on BGA. The other reason is simple to understand: each game sale is also a revenue for an author and an editor (... we've come full circle). [[Contact us]] 09d076571c067045048850f642e222a1e1485eb7 Club 0 7 16 2012-01-23T08:32:39Z Sourisdudesert 1 Redirected page to [[Club Board Game Arena]] wikitext text/x-wiki #REDIRECT [[Club Board Game Arena]] fac244084d01208926b41fc59a051c6ed0c0748a Club Board Game Arena 0 8 17 2012-01-23T08:34:49Z Sourisdudesert 1 Created page with "Board Game Arena's main goal is to promote board games to a large audience. This is the reason why the service is '''free''' for everyone, and '''meant to stay that way'''. A..." wikitext text/x-wiki Board Game Arena's main goal is to promote board games to a large audience. This is the reason why the service is '''free''' for everyone, and '''meant to stay that way'''. As you can imagine, though, this website needs a lot of time and some money to keep things running smoothly. Some players are helping us with contributions or through donations. This website can exist only because of the help from these players, and to thank them we make them members of the "Board Game Arena Club". == How can I become a club member ? == The three main ways to become a club member are: * Make this website known (see referral) * [http://en.boardgamearena.com/#!translationhq Contribute to translate] this website * Support this website with a donation (payment buttons below) {| |Donate '''20€''' (1 year club membership) |Donate '''11€''' (6 months club membership) |Donate '''4€''' (2 months club membership) |- |{one_year_membership} |{six_month_membership} |{two_month_membership} |} ==Why should I join the club ?== The main reason you should join the club is to support this website. Also, as a token of appreciation for their support, we give to club members an access to some statistics: ELO ranking of all members including yours (instead of ), games statistics and specific player statistics for each game. Why can't you just have a standard donation system where I can choose any money amount ? With this "club" system, we try to highlight players who supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classical approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena chooses to set 3 fixed amounts for donations in order to rely on a bigger number of small donors. Is it mandatory to join the club ? Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. What is a beginner account ? When you join Board Game Arena, your get a "beginner account" () for 30 days. This beginner account allow you to view your own ELO ranking () for each game. After 30 days, your account becomes a standard non member account (). What becomes of the money ? Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! 05a49a509de676c255b30cd47a1719a179d3873e 18 17 2012-01-23T08:35:14Z Sourisdudesert 1 /* How can I become a club member ? */ wikitext text/x-wiki Board Game Arena's main goal is to promote board games to a large audience. This is the reason why the service is '''free''' for everyone, and '''meant to stay that way'''. As you can imagine, though, this website needs a lot of time and some money to keep things running smoothly. Some players are helping us with contributions or through donations. This website can exist only because of the help from these players, and to thank them we make them members of the "Board Game Arena Club". == How can I become a club member ? == The three main ways to become a club member are: * Make this website known (see [[referral]]) * [http://en.boardgamearena.com/#!translationhq Contribute to translate] this website * Support this website with a donation (payment buttons below) {| |Donate '''20€''' (1 year club membership) |Donate '''11€''' (6 months club membership) |Donate '''4€''' (2 months club membership) |- |{one_year_membership} |{six_month_membership} |{two_month_membership} |} ==Why should I join the club ?== The main reason you should join the club is to support this website. Also, as a token of appreciation for their support, we give to club members an access to some statistics: ELO ranking of all members including yours (instead of ), games statistics and specific player statistics for each game. Why can't you just have a standard donation system where I can choose any money amount ? With this "club" system, we try to highlight players who supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classical approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena chooses to set 3 fixed amounts for donations in order to rely on a bigger number of small donors. Is it mandatory to join the club ? Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. What is a beginner account ? When you join Board Game Arena, your get a "beginner account" () for 30 days. This beginner account allow you to view your own ELO ranking () for each game. After 30 days, your account becomes a standard non member account (). What becomes of the money ? Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! eb70d022960d5fd26851ae3c461dcf0de7f275f4 19 18 2012-01-23T08:37:03Z Sourisdudesert 1 wikitext text/x-wiki Board Game Arena's main goal is to promote board games to a large audience. This is the reason why the service is '''free''' for everyone, and '''meant to stay that way'''. As you can imagine, though, this website needs a lot of time and some money to keep things running smoothly. Some players are helping us with contributions or through donations. This website can exist only because of the help from these players, and to thank them we make them members of the "Board Game Arena Club". == How can I become a club member ? == The three main ways to become a club member are: * Make this website known (see [[referral]]) * [http://en.boardgamearena.com/#!translationhq Contribute to translate] this website * Support this website with a donation (payment buttons below) {| |Donate '''20€''' (1 year club membership) |Donate '''11€''' (6 months club membership) |Donate '''4€''' (2 months club membership) |- |{one_year_membership} |{six_month_membership} |{two_month_membership} |} ==Why should I join the club ?== The main reason you should join the club is to support this website. Also, as a token of appreciation for their support, we give to club members an access to some statistics: ELO ranking http://en.boardgamearena.com/theme/img/common/rank.png of all members including yours (instead of http://fr.boardgamearena.com/theme/img/common/rankmask.png), games statistics and specific player statistics for each game. ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classical approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena chooses to set 3 fixed amounts for donations in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, your get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allow you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! a12f51dc80f363786676e4551d1473aabc326650 20 19 2012-01-23T08:38:47Z Sourisdudesert 1 /* Why should I join the club ? */ wikitext text/x-wiki Board Game Arena's main goal is to promote board games to a large audience. This is the reason why the service is '''free''' for everyone, and '''meant to stay that way'''. As you can imagine, though, this website needs a lot of time and some money to keep things running smoothly. Some players are helping us with contributions or through donations. This website can exist only because of the help from these players, and to thank them we make them members of the "Board Game Arena Club". == How can I become a club member ? == The three main ways to become a club member are: * Make this website known (see [[referral]]) * [http://en.boardgamearena.com/#!translationhq Contribute to translate] this website * Support this website with a donation (payment buttons below) {| |Donate '''20€''' (1 year club membership) |Donate '''11€''' (6 months club membership) |Donate '''4€''' (2 months club membership) |- |{one_year_membership} |{six_month_membership} |{two_month_membership} |} ==Why should I join the club ?== The main reason you should join the club is to support this website. Also, as a token of appreciation for their support, we give to club members an access to some statistics: ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) of all members including yours (instead of http://fr.boardgamearena.com/theme/img/common/rankmask.png), games statistics and specific player statistics for each game. ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classical approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena chooses to set 3 fixed amounts for donations in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, your get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allow you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! 07d7d56ac7137270da1cb1e701810fd74c30b286 Contact us 0 9 21 2012-01-23T08:40:19Z Sourisdudesert 1 Created page with "'''Board Game Arena''' is located in France. == E-mail contact == contact(at)boardgamearena.com We receive *a lot* of e-mails. Please do not send us an e-mail if: * ... i..." wikitext text/x-wiki '''Board Game Arena''' is located in France. == E-mail contact == contact(at)boardgamearena.com We receive *a lot* of e-mails. Please do not send us an e-mail if: * ... if you want to report a bug, please do it in the corresponding forum. * ... if you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile == Responsible == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 3fa5849702cf1b1b19bd3acb243c32dbb041a4a9 Getting started 0 10 22 2012-01-23T08:42:02Z Sourisdudesert 1 Created page with "With '''Board Game Arena''' you can play within a few clicks. By choosing "[http://en.boardgamearena.com/#!lobby Play now]" on the left menu, you get a list of game tables wa..." wikitext text/x-wiki With '''Board Game Arena''' you can play within a few clicks. By choosing "[http://en.boardgamearena.com/#!lobby Play now]" on the left menu, you get a list of game tables waiting for players. You can join a game by clicking on "View table" then "Join game". If you prefer, you can create a new game table: click on the "Launch table" button that corresponds to the game you want to play, and wait for your opponents. Important to know before you start: * It's much better to join a table already created than to create a new one. * For most popular games you will find opponents at any time. For the others, try to connect around peak hour (around 22h CEST) to maximize your chances. * When you start a game you can't leave the table until it ends. If you leave you will receive a penalty which cause you difficulties to find opponents later. 00acad1f28c408117fc7c59eac2c4627a71d7e72 23 22 2012-01-23T08:42:52Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] With '''Board Game Arena''' you can play within a few clicks. By choosing "[http://en.boardgamearena.com/#!lobby Play now]" on the left menu, you get a list of game tables waiting for players. You can join a game by clicking on "View table" then "Join game". If you prefer, you can create a new game table: click on the "Launch table" button that corresponds to the game you want to play, and wait for your opponents. Important to know before you start: * It's much better to join a table already created than to create a new one. * For most popular games you will find opponents at any time. For the others, try to connect around peak hour (around 22h CEST) to maximize your chances. * When you start a game you can't leave the table until it ends. If you leave you will receive a penalty which cause you difficulties to find opponents later. 860720b52040414f964590d5eb8639fb21735545 Referral 0 11 29 2012-01-23T08:46:41Z Sourisdudesert 1 Created page with "[[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player..." wikitext text/x-wiki [[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player create an account on Board Game Arena from you referrer web address (see below), he becomes one of your '''referees'''. As soon as this referee played few games, you become a member of BGA club. == How to get referees? == To recruit referees, use your personal referrer web address: {sponsorshipurl} New players MUST create an account from this web address to become one of your referees. == How to get many referees? == * [http://boardgamearena.com/#!doc/sponsor?facebook Tell my friends on Facebook] * [http://boardgamearena.com/#!doc/sponsor?twitter Tell my followers on Twitter] * [http://boardgamearena.com/#!doc/sponsor?email Tell my friends by email] * Publish my personal web address in some forum, send it by MSN, ... == How many BGA club membership days can I win? == As soon as your first referee finish his '''third''' game on Board Game Arena, you win a '''1 month''' BGA club membership. ''Note: your account upgrade will be effective within 24 hours.'' As soon as you get '''2 referees''' with at least '''4 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. As soon as you get '''3 referees''' with at least '''5 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. ... and so on: as your referees number grows you can win many free memberships ! == Referees and multiple account == It is of course forbidden to create fake referee accounts to win free memberships. 52e8ab57da4a8638af8bcfdb8ab9549d2bd5ad12 Luokka:Help 14 12 30 2012-01-23T08:47:01Z Sourisdudesert 1 Created page with "Board Game Arena help pages" wikitext text/x-wiki Board Game Arena help pages adc7e25dcb01838e74ee4f7611812cf2762d2767 Browser support 0 13 31 2012-01-23T08:47:52Z Sourisdudesert 1 Created page with "Board Game Arena is using the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download, no plugin t..." wikitext text/x-wiki Board Game Arena is using the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download, no plugin to install, ... Consequently, to play you need to use a recent web browser. Generally speaking, the more recent your web browser is, the more pleasant your game experience on Board Game Arena will be. We officially support the following browsers: * Mozilla Firefox 3.5+ * Google Chrome 4+ * Internet Explorer 8 (partial support for IE7) * Internet Explorer 9 * Safari 4+ (Note: we are NOT supporting iPad or similar tablets) This website uses Javascript technology and your browser's graphics possibilities intensely. Thus, if you want to have the best game experience with Board Game Arena, you should use one of these browsers: * Mozilla Firefox 4 * Google Chrome 10 32bb40e75bb29f6d724daafd30c7906adcadadb7 36 31 2012-01-23T08:52:07Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] Board Game Arena is using the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download, no plugin to install, ... Consequently, to play you need to use a recent web browser. Generally speaking, the more recent your web browser is, the more pleasant your game experience on Board Game Arena will be. We officially support the following browsers: * Mozilla Firefox 3.5+ * Google Chrome 4+ * Internet Explorer 8 (partial support for IE7) * Internet Explorer 9 * Safari 4+ (Note: we are NOT supporting iPad or similar tablets) This website uses Javascript technology and your browser's graphics possibilities intensely. Thus, if you want to have the best game experience with Board Game Arena, you should use one of these browsers: * Mozilla Firefox 4 * Google Chrome 10 03a7ec9371d3e330c0ce5224bdda0f3bf49ec060 Moderation and grades 0 14 32 2012-01-23T08:49:30Z Sourisdudesert 1 Created page with "Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit o..." wikitext text/x-wiki Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a '''mortal'''. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the '''angel''' grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to '''devilkin''' or '''demon'''. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraph can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherub check that Seraph are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Demon''': due to a major terms of use violation, this player is not allowed to do anything on the website during a period of time (or forever ...) * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. 05df6f50ad4723b2aa3c8dfd1e36e49da6dcb38e 35 32 2012-01-23T08:51:55Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a '''mortal'''. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the '''angel''' grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to '''devilkin''' or '''demon'''. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraph can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherub check that Seraph are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Demon''': due to a major terms of use violation, this player is not allowed to do anything on the website during a period of time (or forever ...) * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. 49931146459d3900e8e8099ce554da943f7a304b Game clock 0 15 33 2012-01-23T08:51:23Z Sourisdudesert 1 Created page with "On '''Board Game Arena''' you are playing "live" (real time). Then you has a delay to play your moves. Your initial delay at the beginning of a game is most of the time '''3 ..." wikitext text/x-wiki On '''Board Game Arena''' you are playing "live" (real time). Then you has a delay to play your moves. Your initial delay at the beginning of a game is most of the time '''3 minutes'''. At each turn, or on specific occasion, you get an additional delay. If you have no more time to play, you will get a penalty and can become eligible to be expelled from the game. == Time to think == Your alloted time to think is displayed on the right of your player's name. When it's your turn to player, this time is also displayed at the top of the web page. == Game speed == Table administrator can make a choice between 4 game speed profile: * Fast * Normal * Slow * Without time limit The additional amount of time credited each turn depends on chosen game speed profile. Be careful to check game speed before game start to adapt your timing. Note: playing without time limit is strongly discouraged, except if you are playing with friends or to discover a new game. Remember that without time limit, you can't expel a player that stops playing. == Running out of time == As soon as you run out of time, you get a clock penalty. If you goes over your alloted time by more than 3/4/5 minutes (depending on game speed), any opponent can expel you from the game. Of course, it is strongly advised not to run out of time ... == Move time limit == In addition to your classical time to think, you have a limited time alloted for each move. When it's your turn to play, a red bar appears on top of the web page. It symbolized the remaining time limit for this move. The amount of time depends on game speed. If you goes over this limit, you become eligible to be expelled from the game. == "My opponent is too slow" == Each of us has different expectations on game speed. Please remember that as soon as a table has been set up at a given game speed, each player is allowed to use all his alloted time to think. If you want to play fast, set up or join table with "fast" mode only, but don't force an opponent to play when he has the right to do so. == "I would like to think a little" == If you are in a critical step of the game and want to take some time to think, you can click on the link "I would like to think a little" on the top right of the page. Thus, your opponents will receive a message and won't be thinking you are away from keyboard. Clicking on this link is not mandatory, but we encourage its use for courtesy. 1cfe01c7a6e760c91b41e2d84865f9706010ebdf 34 33 2012-01-23T08:51:44Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] On '''Board Game Arena''' you are playing "live" (real time). Then you has a delay to play your moves. Your initial delay at the beginning of a game is most of the time '''3 minutes'''. At each turn, or on specific occasion, you get an additional delay. If you have no more time to play, you will get a penalty and can become eligible to be expelled from the game. == Time to think == Your alloted time to think is displayed on the right of your player's name. When it's your turn to player, this time is also displayed at the top of the web page. == Game speed == Table administrator can make a choice between 4 game speed profile: * Fast * Normal * Slow * Without time limit The additional amount of time credited each turn depends on chosen game speed profile. Be careful to check game speed before game start to adapt your timing. Note: playing without time limit is strongly discouraged, except if you are playing with friends or to discover a new game. Remember that without time limit, you can't expel a player that stops playing. == Running out of time == As soon as you run out of time, you get a clock penalty. If you goes over your alloted time by more than 3/4/5 minutes (depending on game speed), any opponent can expel you from the game. Of course, it is strongly advised not to run out of time ... == Move time limit == In addition to your classical time to think, you have a limited time alloted for each move. When it's your turn to play, a red bar appears on top of the web page. It symbolized the remaining time limit for this move. The amount of time depends on game speed. If you goes over this limit, you become eligible to be expelled from the game. == "My opponent is too slow" == Each of us has different expectations on game speed. Please remember that as soon as a table has been set up at a given game speed, each player is allowed to use all his alloted time to think. If you want to play fast, set up or join table with "fast" mode only, but don't force an opponent to play when he has the right to do so. == "I would like to think a little" == If you are in a critical step of the game and want to take some time to think, you can click on the link "I would like to think a little" on the top right of the page. Thus, your opponents will receive a message and won't be thinking you are away from keyboard. Clicking on this link is not mandatory, but we encourage its use for courtesy. 82143c3b2c0b2426ec6fbc8637e3c739608e6bfb Rating 0 16 37 2012-01-23T08:53:27Z Sourisdudesert 1 Created page with " == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign . Example: 1648 http://fr.boardgamearena.com/theme..." wikitext text/x-wiki == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign . Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you loose a game. == How is my ELO ranking computed ? == The BGA ELO system is inspired by the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. Let's try to make it simple: the points you win/loose after each game depends on the ranking of your opponents. If you win at a table with opponents stronger than you, your ranking will increase a lot. If you loose at the same table, your ranking will decrease a little. And so on ... In case there are more than 2 players at the table, we take into account the final rank. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 6a46415cd42c733e8f6c21168baf058ec0295e5d 38 37 2012-01-23T08:54:06Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you loose a game. == How is my ELO ranking computed ? == The BGA ELO system is inspired by the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. Let's try to make it simple: the points you win/loose after each game depends on the ranking of your opponents. If you win at a table with opponents stronger than you, your ranking will increase a lot. If you loose at the same table, your ranking will decrease a little. And so on ... In case there are more than 2 players at the table, we take into account the final rank. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 9b83bb61530a3473b4ced7a0e88dac9b3121235a Reputation 0 17 39 2012-01-23T08:57:12Z Sourisdudesert 1 Created page with "[[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To a..." wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. == Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite ! Say at least "hello" and "good luck" at the beginning of the game. "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Stay calm in any circumstances: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happened...), then say you are sorry and leave the game by your own to save your opponents time. * Be a good looser: if your loose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. ccf3fbc039164bc5b2119e80643b725a1e974e87 40 39 2012-01-23T08:57:39Z Sourisdudesert 1 /* Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png */ wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. == Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite ! Say at least "hello" and "good luck" at the beginning of the game. "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Stay calm in any circumstances: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happened...), then say you are sorry and leave the game by your own to save your opponents time. * Be a good looser: if your loose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a http://fr.boardgamearena.com/theme/img/common/reputation_down.png for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. 170ac86366603b161f7deb20712a920f5e460408 Translation guidelines 0 18 41 2012-01-23T08:58:49Z Sourisdudesert 1 Created page with "The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to ena..." wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to enable more people to discover and play boardgames, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10 years old nephew and he doesn't speak english yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated ? == Every string of text for the mainsite interface and for the games interfaces is 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometimes later... == Who should translate ? == This is important : only native speakers of a given language should translate in this language. When translating a game, the translator should know the game and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronomical rules usual for the gaming audience of their country. As Board Game Arena's team's mothertongue is french, we will take charge of the french translations. We will also release the first version of the english translation. As we are not perfect speakers of Shakespeare's language, these translation will be open for review and correction from native english speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot uncorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the translation forum (in english or in french), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into ? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in this language. == I fixed some translations, but they don't appear to have changed on the site ? == Translation files are updated each time we do a new release, so you have to wait until the next BGA release for your changes to appear on the site. Usually this should happen every week or two. == How long will it be possible to change the translations ? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating ? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward among all! == Translation tips == Use the 'TAB' key to go from one text box to another. This is easier than clicking. Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). When in doubt, leave a comment for the next translator. When you fix someone's translation, leave a comment if the reason for the change is not obvious. When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. f785003cc6caeb1c2fd8407b5ea381eb725dad8b 42 41 2012-01-23T08:59:19Z Sourisdudesert 1 /* About context */ wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to enable more people to discover and play boardgames, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10 years old nephew and he doesn't speak english yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated ? == Every string of text for the mainsite interface and for the games interfaces is 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometimes later... == Who should translate ? == This is important : only native speakers of a given language should translate in this language. When translating a game, the translator should know the game and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronomical rules usual for the gaming audience of their country. As Board Game Arena's team's mothertongue is french, we will take charge of the french translations. We will also release the first version of the english translation. As we are not perfect speakers of Shakespeare's language, these translation will be open for review and correction from native english speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot uncorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into ? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in this language. == I fixed some translations, but they don't appear to have changed on the site ? == Translation files are updated each time we do a new release, so you have to wait until the next BGA release for your changes to appear on the site. Usually this should happen every week or two. == How long will it be possible to change the translations ? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating ? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward among all! == Translation tips == Use the 'TAB' key to go from one text box to another. This is easier than clicking. Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). When in doubt, leave a comment for the next translator. When you fix someone's translation, leave a comment if the reason for the change is not obvious. When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. b99f4ae78569524581017328ac85c784a02229ee 43 42 2012-01-23T08:59:44Z Sourisdudesert 1 /* Translation tips */ wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to enable more people to discover and play boardgames, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10 years old nephew and he doesn't speak english yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated ? == Every string of text for the mainsite interface and for the games interfaces is 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometimes later... == Who should translate ? == This is important : only native speakers of a given language should translate in this language. When translating a game, the translator should know the game and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronomical rules usual for the gaming audience of their country. As Board Game Arena's team's mothertongue is french, we will take charge of the french translations. We will also release the first version of the english translation. As we are not perfect speakers of Shakespeare's language, these translation will be open for review and correction from native english speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot uncorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into ? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in this language. == I fixed some translations, but they don't appear to have changed on the site ? == Translation files are updated each time we do a new release, so you have to wait until the next BGA release for your changes to appear on the site. Usually this should happen every week or two. == How long will it be possible to change the translations ? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating ? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward among all! == Translation tips == * Use the 'TAB' key to go from one text box to another. This is easier than clicking. * Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). * When in doubt, leave a comment for the next translator. * When you fix someone's translation, leave a comment if the reason for the change is not obvious. * When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. 2ab2b11b7206e323ff840a76b8a125026e1e0344 Translationguidelines 0 19 44 2012-01-23T09:00:27Z Sourisdudesert 1 Redirected page to [[Translation Guidelines]] wikitext text/x-wiki #REDIRECT [[Translation Guidelines]] 08fc23949a4a226d9c81cccddc96fd7b9f4df0c0 45 44 2012-01-23T09:00:46Z Sourisdudesert 1 Redirected page to [[Translation guidelines]] wikitext text/x-wiki #REDIRECT [[Translation guidelines]] 9f3b026121a1f6a6e96bb1a66cffcbc391a612e0 Järjestelmäviesti:Sitenotice 8 20 46 2012-01-23T09:02:55Z Sourisdudesert 1 Created page with "<div style="font-size: 110%; background-color: ivory; padding: 0.5ex; border: 1px solid black; text-align: center;"> This is a documentation for [http://boardgamearena.com Boa..." wikitext text/x-wiki <div style="font-size: 110%; background-color: ivory; padding: 0.5ex; border: 1px solid black; text-align: center;"> This is a documentation for [http://boardgamearena.com Board Game Arena]: play board games online ! </div> eb809cbdb9e505f0fa399f3b2ceb6a38ecf4d423 Gamehelpstoneage 0 21 47 2012-01-23T09:11:20Z Sourisdudesert 1 Created page with " == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, with your civilizat..." wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, with your civilization cards. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players placed their people on "rings" (1 people per ring). Special rules with 2 players: * only 1 player in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. Special rules with 3 players: * maximum of 2 players in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. ===Use the actions of your placed people=== Each player uses all his placed people in any order, and realize the corresponding actions: * hunting grounds, forest, clay pit, quarry, and river: roll 1 dice per people and take corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * field: increase agriculture level. * tool: get one tool. * hut: get one additional people. * building: buy the building with resources and score points. * civilization card: buy the card with a number of resources depending on its position (1 to 4 resources). ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You can use tool to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If building cost is not specified you win points for each resources used depending on their values (3 for wood, 4 for brick, ...). ===Civilization card details=== Each card brings an immediate advantage (item on the top of the card) and some points during final scoring( item on the bottom of the card). Place the mouse cursor on each card to see details. ===Feed your people=== Each agriculture level bring automatically 1 food. You must use 1 food per people. You can use any resource to feed your people if there is not enough food. If you don't manage to feed your people: 10 points penalty. ===Game end=== The game end when: * there is no more civilization card at the beginning of a round * one building stack is empty at the end of a round During the final scoring round, players win points using their civilization cards. 34d16399aa3a8e5f4902df2ff48e9e81de1c043f Gamehelphaggis 0 22 48 2012-01-23T09:12:40Z Sourisdudesert 1 Created page with " == Goal == Empty your hand before the other players. Additional points may be earned by capturing cards during play and betting that you will be first to shed all of your ca..." wikitext text/x-wiki == Goal == Empty your hand before the other players. Additional points may be earned by capturing cards during play and betting that you will be first to shed all of your cards. == Rules summary == A valid card combination may be a set, a sequence or a bomb: * a "set" is a group of any number of cards of the same rank (ex: one "8", two "5", ...). * a "sequence" is a group of 3 or more cards of the same color with consecutive rank (ex: red 8, red 9, red 10) * a "sequence" can also be a group of 2 or more pairs or larger sets of consecutive rank sharing the same suits between sets (ex: blue 3, green 3, blue 4, green 4, blue 5, green 5) * a "bomb" is one of the following combination (ranked below from lowest to highest): ** 3-5-7-9 (in 4 different colors, "rainbow bomb") ** J-Q ** J-K ** Q-K ** J-Q-K ** 3-5-7-9 (in one color, "suited bomb") Face cards can be used as wild cards to replace any cards in a "set" or a "sequence". Each player starts a round with a Jack, a Queen and a King. These face cards are public. At your turn, you have to play a higher ranking combination with exactly the same type and same number of card that the first combination played. You can also pass. Bombs are an exception: you can play a bomb to beat any combination, except a higher bomb. When all but one player pass in succession, the player who played the highest combination capture all cards played. Important exception: if the highest combination is a bomb, cards are captured by the player with the next higher combination. Then, a new trick starts. The winner of the last trick leads the new trick with any combination. ===Scoring overview=== * At the moment you shed your last card: 5 points per card in the hand of the player who held the most cards. * Cards captured values: 2-4-6-8-10 = 0 point, 3-5-7-9 = 1 point, J = 2 points, Q = 3 points, K = 5 points. * 30 points for a successful "Big bet", 15 points for a successful "Little bet". Points from unsuccessful bets are added to scores of the round's winner and to player(s) who did not bet. da27e6a12b6310619db280f413a7b6b17f319f4f Gamehelppuertorico 0 23 49 2012-01-23T09:14:58Z Sourisdudesert 1 Created page with " == Goal == Be the player with the most victory points at the end of the game by collecting: * victory points earned for buildings * victory point tokens earned at the capta..." wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game by collecting: * victory points earned for buildings * victory point tokens earned at the captain phase * conditional victory points earned at the end of the game for occupied large buildings In case of a tie, the player owning the most doubloons and goods is the winner. == Rules summary == The game is played over several rounds. At the start of a round, the governor selects a role, then each player in turn selects a role. At the end of the round, the governor token moves to the next player. When a role has been chosen, each player in turn plays the action for this role. Only the player who selected the role can use the role privilege. Available roles are the following: * Mayor: colonists set foot in the new world ! Privilege : the mayor can get one more colonist from the supply. Action : in turn, begining with the mayor, players get one colonist from the ship, until it is empty. Then players have to set colonists on their buildings and plantations. * Craftsman: goods are produced. Privilege : the craftsman can produce one more good of his choice. Action : produce goods for your occupied buildings and plantations. * Trader: goods are sold to the trading house. Privilege : the trader earns 1 extra doubloon if he sells. Action : sell one good the trading house doesn't have yet. * Settler : plantations settlement. Privilege : the settler can get a quarry instead of a plantation. Action : get a plantation chosen among those available. * Builder : buildings are bought and built. Privilege : the builder can get a building for 1 doubloon less than the regular price. Action : get a building chosen among those available. * Captain : victory points are won by shipping goods to the old world. Privilege : the captain earns one extra victory points if he ships some goods. Action : select a type of good to load on a cargo ship; 1 victory point is earned for each barrel of this type of good loaded on the ship. * Prospector : gold rush brings money ! Privilege : the prospector gets one doubloon from the bank. Action : none. Please also note that occupied violet buildings have modifier effects for role actions. End of game happens at the end of the round for which one of the following events comes to pass : * there is not enough colonists in the supply to refill the colonist ship at the end of the mayor phase * the last victory point token has been earned during the captain phase * at least one of the players built on his 12th and last free space in the city. == Playing online == Choosing a role : click on the role of your choice in the list on the right of your game board. Mayor role : accept or refuse to get an extra colonist from the supply. Colonists are then automatically recruited by players. Click on a building or a plantation to setup a colonist on it. Click on a colonist to send it back to San Juan (the counter for colonists in San Juan is situated in your player panel on the right of the page). Craftsman role : goods production is automated (counters for goods owned are in the player panel on the right of the page). Then, choose one extra good that you can produce as your privilege. Trader role : choose a good that you want to sell by clicking on the appropriate counter in your player panel. Settler role : click on the plantation (or quarry) that you want. Placement on your board is automated. Builder role : click on the building that you want to buy. Placement on your board is automated. Captain role : click on the cargo boat on which you want to ship some goods. Then choose the type of good that you want to ship by clicking on the appropriate counter in your player panel. At the end of the captain phase, if you have some goods to store (warehouse or windrose), click on the corresponding counter. Stored goods will be highlighted by a red frame. Prospector role : doubloon is earned immediately. Tooltips are available to give information on roles and buildings by hovering over game elements with the mouse. == Available variant == Balanced game: implements two game balance fixes giving each player an equal chance of winning as listed in the [http://en.wikipedia.org/wiki/Puerto_Rico_%28board_game%29 Puerto Rico (board game) Wikipedia page]. * The prices of the Factory and University buildings are swapped so that the Factory costs 8 doubloons and the University costs 7 doubloons * Any player that starts with a corn plantation starts with 1 doubloon less than the players that start with an indigo plantation. Have a good game ! 5a89fda7aa0b6e44383ce70410e470a81e1a8000 Gamehelpunclechestnuttablegype 0 24 50 2012-01-23T09:15:58Z Sourisdudesert 1 Created page with " == Goal == Move all of your pieces to the Home Row opposite your starting position. == Rules summary == On their turn, each player can: 1) Roll one of his pieces and plac..." wikitext text/x-wiki == Goal == Move all of your pieces to the Home Row opposite your starting position. == Rules summary == On their turn, each player can: 1) Roll one of his pieces and place it back where it was 2) Move one of the pieces according to the rules for the face-up side == Movement of the pieces: == * Flame: can move straight or diagonally in all directions * Book: can move straight in all directions * Swords: can move diagonnally in all directions * Tree: can move straight forward or backwards, and diagonally forward * Ear: cannot move at all ! * Hat: can move two steps straight then one step on the side, in all directions (like the knight in chess) A piece can jump over an adjacent piece if there is an open space on the other side of the piece being jumped in the direction of the jumping piece's movement. The first time a piece is jumped over during a turn, it has to be rolled. After a jump, all pieces (except for the hat) can go on jumping as long as there is another valid jump available from the landing space. A piece can freely move within its Home Row as long as it did not leave it. After it leaves though, it can't reenter its Home Row. A piece arrived into his destination Home Row cannot move afterwards. Nonetheless, it can always finish a series of jumps before coming to a rest. Playing online Reordering your pieces: before starting to play, each player can reorder his pieces in his Home Row. To accomplish this, you can drag and drop a piece over another to exchange their positions. When you are done, click on the 'I'm finished' link above the play space. When all players are finished reordering, the game starts. Select a piece: click on a piece. Available destinations for this piece (jumps comprised) are then highlighted. Move a piece: click on the destination you want to move to. After a jump, if it is possible to make another jump, corresponding destinations will be highlighted. You can also stop where you are by clicking on the 'I'm finished' link above the play space. Path selection: in order to move to a given destination, the hat can select between two moves. Either two steps forward then one on the side, or one step on the side then two steps forward. Distinct pieces may be rolled by following one path or another. Choosing the movement you want is done by clicking the link '1->2' or the link '2->1' (selected path is then highlighted), then by clicking the link 'I'm finished' above the play space. Available variants Random start: for a more random game, starting pieces are rolled (Ears are rolled again till getting only moving pieces). Rule of Chaos: to encourage more chaos, a player cannot cross the middle of the board with any of their pieces until their starting Home Row has been emptied: all pieces have to be put in play at the same time. Random start & Rule of Chaos : combination of the two previous variants. Have a good game ! 47a14dc24dc2483015390cae73fbdbd33cb69c3b Gamehelpunclechestnuttablegype 0 24 51 50 2012-01-23T09:16:52Z Sourisdudesert 1 wikitext text/x-wiki == Goal == Move all of your pieces to the Home Row opposite your starting position. == Rules summary == On their turn, each player can: * 1°) Roll one of his pieces and place it back where it was * 2°) Move one of the pieces according to the rules for the face-up side == Movement of the pieces: == * Flame: can move straight or diagonally in all directions * Book: can move straight in all directions * Swords: can move diagonnally in all directions * Tree: can move straight forward or backwards, and diagonally forward * Ear: cannot move at all ! * Hat: can move two steps straight then one step on the side, in all directions (like the knight in chess) A piece can jump over an adjacent piece if there is an open space on the other side of the piece being jumped in the direction of the jumping piece's movement. The first time a piece is jumped over during a turn, it has to be rolled. After a jump, all pieces (except for the hat) can go on jumping as long as there is another valid jump available from the landing space. A piece can freely move within its Home Row as long as it did not leave it. After it leaves though, it can't reenter its Home Row. A piece arrived into his destination Home Row cannot move afterwards. Nonetheless, it can always finish a series of jumps before coming to a rest. == Playing online == '''Reordering your pieces''': before starting to play, each player can reorder his pieces in his Home Row. To accomplish this, you can drag and drop a piece over another to exchange their positions. When you are done, click on the 'I'm finished' link above the play space. When all players are finished reordering, the game starts. '''Select a piece''': click on a piece. Available destinations for this piece (jumps comprised) are then highlighted. '''Move a piece''': click on the destination you want to move to. After a jump, if it is possible to make another jump, corresponding destinations will be highlighted. You can also stop where you are by clicking on the 'I'm finished' link above the play space. '''Path selection''': in order to move to a given destination, the hat can select between two moves. Either two steps forward then one on the side, or one step on the side then two steps forward. Distinct pieces may be rolled by following one path or another. Choosing the movement you want is done by clicking the link '1->2' or the link '2->1' (selected path is then highlighted), then by clicking the link 'I'm finished' above the play space. == Available variants == '''Random start''': for a more random game, starting pieces are rolled (Ears are rolled again till getting only moving pieces). '''Rule of Chaos''': to encourage more chaos, a player cannot cross the middle of the board with any of their pieces until their starting Home Row has been emptied: all pieces have to be put in play at the same time. '''Random start & Rule of Chaos''' : combination of the two previous variants. Have a good game ! e0e1c7bf1bdc07eca3c7b1aa2d2d463ff891abe4 79 51 2012-03-11T14:43:52Z Een 3 wikitext text/x-wiki == Goal == Move all of your pieces to the Home Row opposite your starting position. == Rules summary == On their turn, each player can: # Roll one of his pieces and place it back where it was # Move one of the pieces according to the rules for the face-up side == Movement of the pieces: == * Flame: can move straight or diagonally in all directions * Book: can move straight in all directions * Swords: can move diagonnally in all directions * Tree: can move straight forward or backwards, and diagonally forward * Ear: cannot move at all ! * Hat: can move two steps straight then one step on the side, in all directions (like the knight in chess) A piece can jump over an adjacent piece if there is an open space on the other side of the piece being jumped in the direction of the jumping piece's movement. The first time a piece is jumped over during a turn, it has to be rolled. After a jump, all pieces (except for the hat) can go on jumping as long as there is another valid jump available from the landing space. A piece can freely move within its Home Row as long as it did not leave it. After it leaves though, it can't reenter its Home Row. A piece arrived into his destination Home Row cannot move afterwards. Nonetheless, it can always finish a series of jumps before coming to a rest. == Playing online == '''Reordering your pieces''': before starting to play, each player can reorder his pieces in his Home Row. To accomplish this, you can drag and drop a piece over another to exchange their positions. When you are done, click on the 'I'm finished' link above the play space. When all players are finished reordering, the game starts. '''Select a piece''': click on a piece. Available destinations for this piece (jumps comprised) are then highlighted. '''Move a piece''': click on the destination you want to move to. After a jump, if it is possible to make another jump, corresponding destinations will be highlighted. You can also stop where you are by clicking on the 'I'm finished' link above the play space. '''Path selection''': in order to move to a given destination, the hat can select between two moves. Either two steps forward then one on the side, or one step on the side then two steps forward. Distinct pieces may be rolled by following one path or another. Choosing the movement you want is done by clicking the link '1->2' or the link '2->1' (selected path is then highlighted), then by clicking the link 'I'm finished' above the play space. == Available variants == '''Random start''': for a more random game, starting pieces are rolled (Ears are rolled again till getting only moving pieces). '''Rule of Chaos''': to encourage more chaos, a player cannot cross the middle of the board with any of their pieces until their starting Home Row has been emptied: all pieces have to be put in play at the same time. '''Random start & Rule of Chaos''' : combination of the two previous variants. '''Have a good game !''' cf24749138ddb9e1bb14c0a6a3ff344a8f7e2684 Gamehelpgosu 0 25 52 2012-01-23T09:19:41Z Sourisdudesert 1 Created page with "Need some help ? Join [http://www.gosulair.fr/index.php Gosu player's community on Gosulair.fr] == Victory == You win when you have 3 Victory Points or when you complete a s..." wikitext text/x-wiki Need some help ? Join [http://www.gosulair.fr/index.php Gosu player's community on Gosulair.fr] == Victory == You win when you have 3 Victory Points or when you complete a special victory condition. == Rules summary == During the Round, players play in turns, clockwise. The player with the Advantage Token starts to play. When all players have passed, the Round ends and the Great Battle takes place. ===Great Battle=== Add up the military value of all your goblins. The owner of the most powerful army wins the Battle and scores a Victory Point. In case of a tie during the Battle, the player with the Advantage Token wins. All armies stay put and the players keep their hand of cards. ===One turn, one action=== * Play a goblin. * Mutate a goblin. * Spend 1 Activation Token to draw 1 card. * Spend 2 Activation Tokens to draw 3 cards. * Play an Activation Token on a goblin to activate its power (only 1 Token allowed per card). * Pass (the player who pass has no turn until the next Round.). ===Goblins=== Every goblin in a Level are played from left to right in one row. Rows are ordered by ascending level from the bottom to the top. You can't play more than 5 goblins per row. '''Bakutos (level I)''' * The first Bakuto is always free. * Play a Bakuto is free if a Bakuto from the same clan is already in play. * To play a Bakuto from a new clan, you have to discard 2 cards. '''Heroes (level II)''' * No more Heroes than Bakutos. * In order to play a Hero, a Bakuto from the same clan must be in your army (no need to have him above the Bakuto). '''Ōzekis (level III)''' * No more Ōzekis than Heroes. * In order to play an Ōzeki, a Hero and a Bakuto from the same clan must be in your army. ===Mutation=== You can mutate a goblin in another different goblin on the same Level if you pay the Mutation Cost of the card: Name the card to mutate. Name et set aside the card that comes into play. Discard the mutated card. Apply any triggered effect. Play the mutating card. Apply any triggered effect. Resolve Mutating effect. A mutated carte is not « destroyed ». Zombie Mutation: the mutating goblin may come from the discard pile. Goblins powers & details A FREE card has no cards above nor on its right. Carte emprisonnée : retournée, elle n'a ni pouvoir ni valeur militaire ni clan, mais conserve son nom et sa place dans l'armée. Costs : the card costs (play, mutate, ...) are payed with cards discarded from your hand. When the draw pile is empty and you have to draw, you shuffle the discard pile and make it the new draw pile. The bonus mentionned into brackets (+...) apply if some opponent has more victory points. The discard pile receive the discarded, destroyed or mutated cards. Any player can browse it anytime. If a player wins his third Victory Point in a Round, he wins the game immediatly. Board Game Arena would like to thanks mAxAttAck de gosulair.fr for this game help. 6b6358222feb2e53c7791d8d0c33a17076519651 53 52 2012-01-23T09:20:49Z Sourisdudesert 1 wikitext text/x-wiki Need some help ? Join [http://www.gosulair.fr/index.php Gosu player's community on Gosulair.fr] == Victory == You win when you have 3 Victory Points or when you complete a special victory condition. == Rules summary == During the Round, players play in turns, clockwise. The player with the Advantage Token starts to play. When all players have passed, the Round ends and the Great Battle takes place. ===Great Battle=== Add up the military value of all your goblins. The owner of the most powerful army wins the Battle and scores a Victory Point. In case of a tie during the Battle, the player with the Advantage Token wins. All armies stay put and the players keep their hand of cards. ===One turn, one action=== * Play a goblin. * Mutate a goblin. * Spend 1 Activation Token to draw 1 card. * Spend 2 Activation Tokens to draw 3 cards. * Play an Activation Token on a goblin to activate its power (only 1 Token allowed per card). * Pass (the player who pass has no turn until the next Round.). ===Goblins=== Every goblin in a Level are played from left to right in one row. Rows are ordered by ascending level from the bottom to the top. You can't play more than 5 goblins per row. '''Bakutos (level I)''' * The first Bakuto is always free. * Play a Bakuto is free if a Bakuto from the same clan is already in play. * To play a Bakuto from a new clan, you have to discard 2 cards. '''Heroes (level II)''' * No more Heroes than Bakutos. * In order to play a Hero, a Bakuto from the same clan must be in your army (no need to have him above the Bakuto). '''Ōzekis (level III)''' * No more Ōzekis than Heroes. * In order to play an Ōzeki, a Hero and a Bakuto from the same clan must be in your army. ===Mutation=== You can mutate a goblin in another different goblin on the same Level if you pay the Mutation Cost of the card: * Name the card to mutate. * Name et set aside the card that comes into play. * Discard the mutated card. * Apply any triggered effect. * Play the mutating card. * Apply any triggered effect. * Resolve Mutating effect. A mutated carte is not « destroyed ». Zombie Mutation: the mutating goblin may come from the discard pile. ===Goblins powers & details=== A FREE card has no cards above nor on its right. Trapped card : no power, no value, no clan. It keeps its name and its place in the army. Costs : the card costs (play, mutate, ...) are payed with cards discarded from your hand. When the draw pile is empty and you have to draw, you shuffle the discard pile and make it the new draw pile. The bonus mentionned into brackets (+...) apply if some opponent has more victory points. The discard pile receive the discarded, destroyed or mutated cards. Any player can browse it anytime. If a player wins his third Victory Point in a Round, he wins the game immediatly. Board Game Arena would like to thanks mAxAttAck de gosulair.fr for this game help. 2de59874bb4c8b039c82523b624075e1a4da29c1 66 53 2012-02-06T01:12:27Z Jleidich 100 /* Mutation */ wikitext text/x-wiki Need some help ? Join [http://www.gosulair.fr/index.php Gosu player's community on Gosulair.fr] == Victory == You win when you have 3 Victory Points or when you complete a special victory condition. == Rules summary == During the Round, players play in turns, clockwise. The player with the Advantage Token starts to play. When all players have passed, the Round ends and the Great Battle takes place. ===Great Battle=== Add up the military value of all your goblins. The owner of the most powerful army wins the Battle and scores a Victory Point. In case of a tie during the Battle, the player with the Advantage Token wins. All armies stay put and the players keep their hand of cards. ===One turn, one action=== * Play a goblin. * Mutate a goblin. * Spend 1 Activation Token to draw 1 card. * Spend 2 Activation Tokens to draw 3 cards. * Play an Activation Token on a goblin to activate its power (only 1 Token allowed per card). * Pass (the player who pass has no turn until the next Round.). ===Goblins=== Every goblin in a Level are played from left to right in one row. Rows are ordered by ascending level from the bottom to the top. You can't play more than 5 goblins per row. '''Bakutos (level I)''' * The first Bakuto is always free. * Play a Bakuto is free if a Bakuto from the same clan is already in play. * To play a Bakuto from a new clan, you have to discard 2 cards. '''Heroes (level II)''' * No more Heroes than Bakutos. * In order to play a Hero, a Bakuto from the same clan must be in your army (no need to have him above the Bakuto). '''Ōzekis (level III)''' * No more Ōzekis than Heroes. * In order to play an Ōzeki, a Hero and a Bakuto from the same clan must be in your army. ===Mutation=== You can mutate a goblin in another different goblin on the same Level if you pay the Mutation Cost of the card: * Name the card to mutate. * Name and set aside the card that comes into play. * Discard the mutated card. * Apply any triggered effect. * Play the mutating card. * Apply any triggered effect. * Resolve Mutating effect. A mutated card is not « destroyed ». Zombie Mutation: the mutating goblin may come from the discard pile. ===Goblins powers & details=== A FREE card has no cards above nor on its right. Trapped card : no power, no value, no clan. It keeps its name and its place in the army. Costs : the card costs (play, mutate, ...) are payed with cards discarded from your hand. When the draw pile is empty and you have to draw, you shuffle the discard pile and make it the new draw pile. The bonus mentionned into brackets (+...) apply if some opponent has more victory points. The discard pile receive the discarded, destroyed or mutated cards. Any player can browse it anytime. If a player wins his third Victory Point in a Round, he wins the game immediatly. Board Game Arena would like to thanks mAxAttAck de gosulair.fr for this game help. a62e06b222c75ef7b96e85f1dc657b36d24fcd85 67 66 2012-02-06T01:14:28Z Jleidich 100 /* Goblins powers & details */ wikitext text/x-wiki Need some help ? Join [http://www.gosulair.fr/index.php Gosu player's community on Gosulair.fr] == Victory == You win when you have 3 Victory Points or when you complete a special victory condition. == Rules summary == During the Round, players play in turns, clockwise. The player with the Advantage Token starts to play. When all players have passed, the Round ends and the Great Battle takes place. ===Great Battle=== Add up the military value of all your goblins. The owner of the most powerful army wins the Battle and scores a Victory Point. In case of a tie during the Battle, the player with the Advantage Token wins. All armies stay put and the players keep their hand of cards. ===One turn, one action=== * Play a goblin. * Mutate a goblin. * Spend 1 Activation Token to draw 1 card. * Spend 2 Activation Tokens to draw 3 cards. * Play an Activation Token on a goblin to activate its power (only 1 Token allowed per card). * Pass (the player who pass has no turn until the next Round.). ===Goblins=== Every goblin in a Level are played from left to right in one row. Rows are ordered by ascending level from the bottom to the top. You can't play more than 5 goblins per row. '''Bakutos (level I)''' * The first Bakuto is always free. * Play a Bakuto is free if a Bakuto from the same clan is already in play. * To play a Bakuto from a new clan, you have to discard 2 cards. '''Heroes (level II)''' * No more Heroes than Bakutos. * In order to play a Hero, a Bakuto from the same clan must be in your army (no need to have him above the Bakuto). '''Ōzekis (level III)''' * No more Ōzekis than Heroes. * In order to play an Ōzeki, a Hero and a Bakuto from the same clan must be in your army. ===Mutation=== You can mutate a goblin in another different goblin on the same Level if you pay the Mutation Cost of the card: * Name the card to mutate. * Name and set aside the card that comes into play. * Discard the mutated card. * Apply any triggered effect. * Play the mutating card. * Apply any triggered effect. * Resolve Mutating effect. A mutated card is not « destroyed ». Zombie Mutation: the mutating goblin may come from the discard pile. ===Goblins powers & details=== A FREE card has no cards above it nor to its right. Trapped card : no power, no value, no clan. It keeps its name and place in the army. Costs : the card costs (play, mutate, ...) are payed with cards discarded from your hand. When the draw pile is empty and you have to draw, you shuffle the discard pile and make it the new draw pile. The bonus mentioned in brackets (+...) apply if some opponent has more victory points. The discard pile receives the discarded, destroyed or mutated cards. Any player can browse it anytime. If a player wins his third Victory Point in a Round, he wins the game immediatly. Board Game Arena would like to thanks mAxAttAck de gosulair.fr for this game help. aeee202f11e1fef25f50b04ff56e9895ee5263d2 Gamehelptroyes 0 26 54 2012-01-23T09:25:01Z Sourisdudesert 1 Created page with " == Goal == Be the player with the most victory points at the end of the game. == Rules summary == (This rules summary is based on the game help written by [http://boardgam..." wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game. == Rules summary == (This rules summary is based on the game help written by [http://boardgamegeek.com/user/pregremlin Andrew Agard] for Board Game Geek, under Creative Commons license. Please see [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] here for details. Thank you Andrew !) ===Initial Placement=== * In clockwise order place a citizen in empty space of one building * Continue in counter clockwise order from last player and so on until all citizens placed ===Game play=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! fbdfaed8129e77bd038dee3623968940c24674a0 Gamehelptobago 0 27 55 2012-01-23T09:26:14Z Sourisdudesert 1 Created page with " == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player..." wikitext text/x-wiki == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not is sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map indicate show where the treasure can be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or one space only when your ATV goes from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after sharing the treasure for which the deck of treasure cards has been exhausted. Have a good game ! 1cf926fcbeaf776e08a8d7a0e3228b35b9f0c542 81 55 2012-03-11T14:48:48Z Een 3 wikitext text/x-wiki == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not is sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map indicate show where the treasure can be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or one space only when your ATV goes from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after sharing the treasure for which the deck of treasure cards has been exhausted. '''Have a good game !''' b3a0671ef523f43497b67a815ac6f839a04dbd1b 87 81 2012-03-31T00:48:04Z Avron 453 /* 1) Play a clue card from his hand */ wikitext text/x-wiki == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map indicate where the treasure can be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or one space only when your ATV goes from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after sharing the treasure for which the deck of treasure cards has been exhausted. '''Have a good game !''' 1ce82af1085c3662b22e442d6634e1ed2c342517 Gamehelpamyitis 0 28 56 2012-01-23T09:29:49Z Sourisdudesert 1 Created page with " == Victory == The player with the most victory points (VP) wins the game. == Rules summary == Thank you [http://boardgamegeek.com/user/sdetoni Steven] for this game help ..." wikitext text/x-wiki == Victory == The player with the most victory points (VP) wins the game. == Rules summary == Thank you [http://boardgamegeek.com/user/sdetoni Steven] for this game help (original file is [http://boardgamegeek.com/filepage/64197/synopses-of-amyitis-rules there]). Game starts, and recycles the 3 phases, setup, action, maintenance, until: * 4 or less garden tiles are left after a maintenance phase for 3/4 players. * 3 or less garden tiles are left after a maintenance phase for 2 players. ===Setup Phase=== Shuffle all Craftsmen cards and Layout as below. * 2 players: 2 groups of three cards. * 3 players: 3 groups of three cards. * 4 players: 4 groups of three cards. Players with Bankers earn Income (1 coin, 2 coins, or 2 coins & 2 VPs). ===Action Phase=== '''Passing''' Place cube on Pass space; each time that player Passes afterward, he earns 1 coin. '''Purchasing Workers''' Recruit a Craftsman from a set; the first in a set (3 horizontal cards) costs no coins, the second cost 1 coin, and the third costs 2 coins. Turn over card to display coin upon chosen card, which signifies the cost of the next card. Merchant: Take 1 camel into the players supply. Engineer: Place 1 cube of a player on an irrigation marker and get 2 VPs. The cube placement must trace an unbroken path of irrigation cubes to the river. Priest: Place 1 cube of a player into one Temple’s line of petitioners (each Temple will earn something in the Maintenance Phase). Place cube from left and shift all other cubes to the right. Cubes that drop off from the right are placed back into supply. Farmer: Place 1 cube of a player on the left most open field space and take that Resource into Supply. If that Resource is Wine, the field is cleared and whoever had a majority of cubes in that field gets a Gardener card if one is available. If tied, the cube colour furthest to the right wins the gardener card. '''Trading/Caravanning''' Move the Caravan by paying at least 1 Camel token and moving as many spaces as Camels paid plus up to as many spaces as allowed by the current Caravan card. A Caravan cannot be moved to a place where a player cannot fulfil the demands of that city. If the Caravan moves into Babylon either 1 Resource token must be given up for 3 VPs or 2 resource tokens can be given for 6 VPs; that player also places one irrigation cube. In some cities a player can hire Bankers and Caravaneers, as well as expand their Palace (bonus one-time VPs are added when cards are purchased). Pay the required resource to acquire these upgrade cards. In four of the cities there are Plants available. Some Plants are better than others and cost more (i.e. 2 resource tokens instead of 1). There are four levels to the Hanging Gardens, with level 1 Plants being able to be planted on its first level. Level 2 Plants may also be planted in level 1 Hanging Gardens. Gardeners can improve a Plant’s quality, allowing level 1 Plants to be planted on the Wonder’s second level or even its third; or allowing a level 2 Plant to be planted on the third level. That is, 1 or more gardener cards can be used on a plant to increases its quality. The Hanging Gardens’ fourth level requires a level 3 plant only. As soon as Plants are paid for, they are planted. The player who bought the Plant gains the prizes on the Wonder’s tile. A player who controls the irrigation around that tile (i.e. the most number of their colour cubes) scores VPs equal to the Plant’s initial level (not including spent gardener cards). If there is tie, then no VPs are given. Neutral grey cubes are added to empty irrigation spots of adjacent planted tile areas. Once bonuses and VPs have been award, the plant card is turned over in the caravan board to show a different plant on offer for that city/area. ===Maintenance Phase=== Temple Procession is controlled by the last player in the round; they place 1 cube of that players colour into one of the Temples of their choice, and 1 neutral cube into each of the other temples (i.e. 2 grey cubes total). Temples provide benefits to 1 player in a 2 player game, or 2 players in a 3-4 player game; ties for majority are broken by whose cube is closest to the right/exit. Award bonuses for each temple: Ishtar 1st Gets choice of a camel token or a coin. 2nd Gets what the 1st place didn’t want. Marduk 1st Gets 2 VPs. 2nd Gets 1 VPs. Tammouz 1st Gets to add a clue to the resource field of their choice. 2nd Gets to swap one of their resource tokens from supply, but not for wine. Players check goods in their Supply for resource spoilage and adjust the token number they can keep between major game rounds. Level 0,1 Caravaneers can keep up to 2 resource tokens. Level 2 Caravaneers can keep up to 4 resource tokens. The Starting Player card is passed clockwise. Re-shuffle craftsmen cards; go to setup phase to complete another game round. ===End Game=== Game ends when the follow conditions are met for the number people playing. 2 Players: When 3 garden tiles are left or less at the end of major game round. 5 or 6 Wonder tiles gained add additional 5 VPs, 7 Wonder tiles gained, add 10 VPs. 3 Players: When 4 garden tiles are left or less at the end of major game round. 4 or 5 Wonder tiles gained add additional 5 VPs, 6 Wonder tiles gained, add 10 VPs. 4 Players: When 4 garden tiles are left or less at the end of major game round. 3 or 4 Wonder tiles gained add additional 5 VPs, 5 Wonder tiles gained, add 10 VPs. Each player scores 1 additional VP for each resource token remaining in his stock (nothing for the camels and the coins). Total points = Current VPs + Resources Token Left + Bonus for number of garden tiles planted. 7f310513787f643d738ab499101925bd3ba58b17 Translation guidelines 0 18 57 43 2012-01-23T18:24:35Z Een 3 /* I fixed some translations, but they don't appear to have changed on the site ? */ wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to enable more people to discover and play boardgames, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10 years old nephew and he doesn't speak english yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated ? == Every string of text for the mainsite interface and for the games interfaces is 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometimes later... == Who should translate ? == This is important : only native speakers of a given language should translate in this language. When translating a game, the translator should know the game and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronomical rules usual for the gaming audience of their country. As Board Game Arena's team's mothertongue is french, we will take charge of the french translations. We will also release the first version of the english translation. As we are not perfect speakers of Shakespeare's language, these translation will be open for review and correction from native english speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot uncorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into ? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in this language. == I fixed some translations, but they don't appear to have changed on the site ? == Translation files are updated by a nightly batch, so you just have to wait till the morrow for your changes to appear on the site. == How long will it be possible to change the translations ? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating ? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward among all! == Translation tips == * Use the 'TAB' key to go from one text box to another. This is easier than clicking. * Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). * When in doubt, leave a comment for the next translator. * When you fix someone's translation, leave a comment if the reason for the change is not obvious. * When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. f7af8e4384b62aa3f98aed12555045fe22559d9a 63 57 2012-01-28T05:00:40Z Azraeldeadlight 63 /* Who should translate ? */ wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena in any language, in order to enable more people to discover and play boardgames, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10 years old nephew and he doesn't speak english yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated ? == Every string of text for the mainsite interface and for the games interfaces is 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometimes later... == Who should translate ? == This is important: only native speakers of a given language should translate in this language. When translating a game, the translator should know the game and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronominal rules usual for the gaming audience of their country. As Board Game Arena's team's mother tongue is French, we will take charge of the French translations. We will also release the first version of the English translation. As we are not perfect speakers of Shakespeare's language, these translations will be open for review and correction by native English speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot uncorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into ? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in this language. == I fixed some translations, but they don't appear to have changed on the site ? == Translation files are updated by a nightly batch, so you just have to wait till the morrow for your changes to appear on the site. == How long will it be possible to change the translations ? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating ? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward among all! == Translation tips == * Use the 'TAB' key to go from one text box to another. This is easier than clicking. * Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). * When in doubt, leave a comment for the next translator. * When you fix someone's translation, leave a comment if the reason for the change is not obvious. * When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. 03a81a5f108268605fffaeaf1179a98eeeed1e12 68 63 2012-02-07T07:32:09Z Laszlosaurus 116 Great idea to wikify everything! wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena into any language, in order to enable more people to discover and play board games, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10-year-old nephew and he doesn't speak English yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated? == Every string of text for the main site interface and for the games' interfaces are 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometime later... == Who should translate? == This is important: only translate text into a given language if you are a native speaker of the language. When translating a game, the translator should know the game thoroughly and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronominal rules usual for the gaming audience of their country. As Board Game Arena's team's mother tongue is French, we will take charge of the French translations. We will also release the first version of the English translation. As we are not perfect speakers of Shakespeare's language, these translations will be open for review and correction by native English speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot incorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in the language. == I fixed some translations, but they don't appear to have changed on the site == Translation files are updated nightly, so you just have to wait till the morrow for your changes to appear on the site. == How long will it be possible to change the translations? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward of all! == Translation tips == * Use the 'TAB' key to go from one text box to another. This is easier than clicking. * Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). * When in doubt, leave a comment for the next translator. * When you fix someone's translation, leave a comment if the reason for the change is not obvious. * When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. 5342971093e24707f58f475c7b88ca60b9f6c8e5 Gamehelpstoneage 0 21 58 47 2012-01-26T03:44:39Z Oedin 35 /* Game end */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, with your civilization cards. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players placed their people on "rings" (1 people per ring). Special rules with 2 players: * only 1 player in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. Special rules with 3 players: * maximum of 2 players in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. ===Use the actions of your placed people=== Each player uses all his placed people in any order, and realize the corresponding actions: * hunting grounds, forest, clay pit, quarry, and river: roll 1 dice per people and take corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * field: increase agriculture level. * tool: get one tool. * hut: get one additional people. * building: buy the building with resources and score points. * civilization card: buy the card with a number of resources depending on its position (1 to 4 resources). ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You can use tool to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If building cost is not specified you win points for each resources used depending on their values (3 for wood, 4 for brick, ...). ===Civilization card details=== Each card brings an immediate advantage (item on the top of the card) and some points during final scoring( item on the bottom of the card). Place the mouse cursor on each card to see details. ===Feed your people=== Each agriculture level bring automatically 1 food. You must use 1 food per people. You can use any resource to feed your people if there is not enough food. If you don't manage to feed your people: 10 points penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round During the final scoring round, players win points using their civilization cards. f95d7d0ce2d15cea166eddeeb542ba0fc8c1d59c 59 58 2012-01-26T03:46:04Z Oedin 35 /* Feed your people */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, with your civilization cards. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players placed their people on "rings" (1 people per ring). Special rules with 2 players: * only 1 player in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. Special rules with 3 players: * maximum of 2 players in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. ===Use the actions of your placed people=== Each player uses all his placed people in any order, and realize the corresponding actions: * hunting grounds, forest, clay pit, quarry, and river: roll 1 dice per people and take corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * field: increase agriculture level. * tool: get one tool. * hut: get one additional people. * building: buy the building with resources and score points. * civilization card: buy the card with a number of resources depending on its position (1 to 4 resources). ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You can use tool to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If building cost is not specified you win points for each resources used depending on their values (3 for wood, 4 for brick, ...). ===Civilization card details=== Each card brings an immediate advantage (item on the top of the card) and some points during final scoring( item on the bottom of the card). Place the mouse cursor on each card to see details. ===Feed your people=== Each agriculture level automatically give you 1 food. You must use 1 food per person. You can use any resource to feed your people if there is not enough food. If you don't manage to feed your people, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round During the final scoring round, players win points using their civilization cards. aa2efd8d0fcce02506a62d8dae4e1b0669bb8239 60 59 2012-01-26T03:47:06Z Oedin 35 /* Buildings */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, with your civilization cards. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players placed their people on "rings" (1 people per ring). Special rules with 2 players: * only 1 player in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. Special rules with 3 players: * maximum of 2 players in each of the following: forest, clay pit, quarry, and river. * only 2 of the 3 places: tool maker, hut, and field may be filled in each round. ===Use the actions of your placed people=== Each player uses all his placed people in any order, and realize the corresponding actions: * hunting grounds, forest, clay pit, quarry, and river: roll 1 dice per people and take corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * field: increase agriculture level. * tool: get one tool. * hut: get one additional people. * building: buy the building with resources and score points. * civilization card: buy the card with a number of resources depending on its position (1 to 4 resources). ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You can use tool to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not specified, you win points for each of the resources used, depending on their values (3 for wood, 4 for brick, ...). ===Civilization card details=== Each card brings an immediate advantage (item on the top of the card) and some points during final scoring( item on the bottom of the card). Place the mouse cursor on each card to see details. ===Feed your people=== Each agriculture level automatically give you 1 food. You must use 1 food per person. You can use any resource to feed your people if there is not enough food. If you don't manage to feed your people, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round During the final scoring round, players win points using their civilization cards. 6e501537cfc05012c6080c793b3e5560152b5039 97 60 2012-05-18T23:55:17Z Spacediver 718 slight rewrite, according to first learning experience wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). Special rules for game with 2 players only: * no more than 1 player may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field Special rules for game with 3 players only: * no more than 2 players may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * ''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acuired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). 97b0aa912d4b9b69c2f7376b22be6e9e7072bcb4 98 97 2012-05-18T23:56:20Z Spacediver 718 /* Use the actions of your placed people */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). Special rules for game with 2 players only: * no more than 1 player may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field Special rules for game with 3 players only: * no more than 2 players may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * '''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acuired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). 287ea7fa1fd741d4cd21c7b7acc067293f636bc6 99 98 2012-05-18T23:57:16Z Spacediver 718 /* Civilization card details */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). Special rules for game with 2 players only: * no more than 1 player may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field Special rules for game with 3 players only: * no more than 2 players may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * '''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). 8055236dec4e47f0d97b4a11798e1d8683139159 Contact us 0 9 61 21 2012-01-28T04:56:06Z Azraeldeadlight 63 /* E-mail contact */ wikitext text/x-wiki '''Board Game Arena''' is located in France. == E-mail contact == contact(at)boardgamearena.com We receive *a lot* of e-mails. Please do not send us an e-mail if: * ... you want to report a bug, please do it in the corresponding forum. * ... you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile == Responsible == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 b19d027459acac0ecff2c30a0bf71a353da4d5e0 Help 0 4 62 13 2012-01-28T04:56:47Z Azraeldeadlight 63 /* Detailed help */ wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailed help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] c6067b5fc3f21568031e1b54885fd097134d4f1d 77 62 2012-03-01T16:30:05Z Yserbius123 229 Added list of games wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailed help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] == Games == * [[Gamehelphaggis|Haggis]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpdominion|Dominion]] * [[Gamehelpyearofthedragon|In the Year of the Dragon]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpgosu|Gosu]] * [[Gamehelppuertorico|Puerto Rico]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelptobago|Tobago]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpunclechesnutstablegype|Uncle Chesnut's Table Gype]] 7d07756ae02893aca53910e7f887b90075704c79 Faq 0 3 64 24 2012-02-02T00:26:04Z Sunrise 21 /* What is forbidden on Board Game Arena? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 30db44a0c18a4716b60b3f10ceeebfaab2fd2f29 74 64 2012-02-21T23:26:39Z Sunrise 21 /* Meeting players and starting games */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: When you win a 2 players game: 1 point. When you win a 3 players game: 1.5 points. When you win a 4 players game: 2 points. etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. abd36ffcfe6194a01564f66ee1d74aadb48ddd82 75 74 2012-02-21T23:27:11Z Sunrise 21 /* Meeting players and starting games */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the table, most of the time it's because there is not yet enough players around the table for playing this game. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. edeaf919c942259f6571aeb8fcdae43a6f0ce9e4 100 75 2012-05-19T00:05:58Z Spacediver 718 /* I joined a game. When does the game start? */ (removed confusion of 'around the table' with 'joined the table') wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamlist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. a22edcd73f518e3d5b1421a84dfce0e7bd9832b4 Gamehelpdominion 0 29 65 2012-02-02T00:54:39Z Sunrise 21 Created page with "'''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being deplete..." wikitext text/x-wiki '''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being depleted. '''Setup''' Each player starts the game with a deck of 10 cards (3 ''Estates'', and 7 ''Copper''). The deck is shuffled and 5 cards are drawn to form the player's hand. '''Overview''' The A-B-C rule makes it easy to remember the play order. On a player's turn: A: Action The player may play an action card. Each player is allowed 1 action per turn by default. Certain action cards provide additional actions. B: Buy After the action phase is over, the player may play buy a card from the general supply. Bought cards are placed in the player's discard pile. Each player is allowed 1 buy per turn by default. Certain action cards provide additional buys. C: Cleanup After the buy phase is over, the player places all cards (played cards and in hand cards) into the discard pile. Five new cards are drawn from the player's deck. If there aren't enough cards to form a full hand, the discard pile is reshuffled and forms the new deck. The game progresses in clockwise order until ending conditions are met. '''Tie-breaker''' If 2 or more players have the same number of victory points, the player that had the fewest number of turns wins. If these is still a tie, all tied players rejoice in a shared victory. 805fe71c99ac402dc86dcc9baf048905fac8d1a2 71 65 2012-02-18T20:50:29Z Camaroman 213 wikitext text/x-wiki '''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being depleted. '''Setup''' Each player starts the game with a deck of 10 cards (3 ''Estates'', and 7 ''Copper''). The deck is shuffled and 5 cards are drawn to form the player's hand. '''Overview''' The A-B-C rule makes it easy to remember the play order. On a player's turn: A: Action The player may play an action card. Each player is allowed 1 action per turn by default. Certain action cards provide additional actions. B: Buy After the action phase is over, the player may play buy a card from the general supply. Bought cards are placed in the player's discard pile. Each player is allowed 1 buy per turn by default. Certain action cards provide additional buys. C: Cleanup After the buy phase is over, the player places all cards (played cards and in hand cards) into the discard pile. Five new cards are drawn from the player's deck. If there aren't enough cards to form a full hand, the discard pile is reshuffled and forms the new deck. The game progresses in clockwise order until ending conditions are met. '''Tie-breaker''' If 2 or more players have the same number of victory points, the player that had the fewest number of turns wins. If there is still a tie, all tied players rejoice in a shared victory. bc9f64e7b84f58a37339e487710ed67e3593ba84 76 71 2012-02-26T01:36:51Z Dclaw25 248 wikitext text/x-wiki '''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being depleted. '''Setup''' Each player starts the game with a deck of 10 cards (3 ''Estates'', and 7 ''Copper''). The deck is shuffled and 5 cards are drawn to form the player's hand. '''Overview''' The A-B-C rule makes it easy to remember the play order. On a player's turn: A: Action: The player may play an action card. Each player is allowed 1 action per turn by default. Certain action cards provide additional actions. B: Buy: After the action phase is over, the player may play buy a card from the general supply. Bought cards are placed in the player's discard pile. Each player is allowed 1 buy per turn by default. Certain action cards provide additional buys. C: Cleanup: After the buy phase is over, the player places all cards (played cards and in hand cards) into the discard pile. Five new cards are drawn from the player's deck. If there aren't enough cards to form a full hand, the discard pile is reshuffled and forms the new deck. The game progresses in clockwise order until ending conditions are met. '''Tie-breaker''' If 2 or more players have the same number of victory points, the player that had the fewest number of turns wins. If there is still a tie, all tied players rejoice in a shared victory. 98e728c742c8883c076384792bd60f9fb34bc840 Browser support 0 13 69 36 2012-02-07T08:43:53Z Laszlosaurus 116 emphasized recommended browsers and provided download links wikitext text/x-wiki [[Category:Help]] <h3>For the best experience, we recommend:</h3> * '''Google Chrome 10+''' [http://www.google.com/chrome Windows] [http://www.google.com/chrome?platform=mac Mac] [http://www.google.com/chrome?platform=linux Linux] * '''Mozilla Firefox 4+''' [http://www.mozilla.org/products/firefox/ Windows] [http://www.mozilla.org/products/firefox/ Mac] [http://www.mozilla.org/products/firefox/ Linux] Board Game Arena takes advantage of the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download or update, no plugins to install, etc. Consequently, to play you need to use a modern web browser. Generally speaking, the more recent your web browser is, the more pleasant your game experience on Board Game Arena will be. This website makes intensive use of Javascript and your browser's graphics capabilities. Thus, if you want to have the best gaming experience with Board Game Arena, you should use one of the browsers listed above. However, we officially support the following browsers: * Google Chrome 4+ * Mozilla Firefox 3.5+ * Internet Explorer 9 * Internet Explorer 8 (partial support for IE7) * Safari 4+ (Note: We do NOT support play on iPads or similar tablets.) 3872e0f01d029cea980494755aa10bf990d93e78 Grade 0 30 70 2012-02-09T01:48:50Z Cthulpiss 110 Created page with "Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit o..." wikitext text/x-wiki Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a mortal. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the angel grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to devilkin or demon. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraph can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherub check that Seraph are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Demon''': due to a major terms of use violation, this player is not allowed to do anything on the website during a period of time (or forever ...) * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. 71f29ae0ffe6a00b8e4a457aacd391fde8cee61d Rating 0 16 72 38 2012-02-20T05:32:55Z Amagila 220 wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How is my ELO ranking computed ? == The BGA ELO system is inspired by the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. Let's try to make it simple: the points you win/lose after each game depends on the ranking of your opponents. If you win at a table with opponents stronger than you, your ranking will increase a lot. If you lose at the same table, your ranking will decrease a little. And so on ... In case there are more than 2 players at the table, we take into account the final rank. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO c3ec166f7f82b32d6e93d7108888b93f71461a40 Reputation 0 17 73 40 2012-02-20T05:34:23Z Amagila 220 wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. == Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite ! Say at least "hello" and "good luck" at the beginning of the game. "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Stay calm in any circumstances: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happened...), then say you are sorry and leave the game by your own to save your opponents time. * Be a good loser: if your lose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a http://fr.boardgamearena.com/theme/img/common/reputation_down.png for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. 82e240a99d58813f6fa5c065f5d6a831f89af9d4 Gamehelpdragonheart 0 31 78 2012-03-02T15:38:38Z Yserbius123 229 New page wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 2 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it it. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards were played ends the game. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus two extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. daaa8873bc1834aa5742568b1a229c985f2a521d 95 78 2012-04-23T19:30:36Z Sourisdudesert 1 wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 2 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it it. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards were played ends the game. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. a6a9718046f5d9072f2785309784178d640abc81 96 95 2012-04-30T20:53:02Z Christopheryoder 621 /* Cards */ wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 2 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards were played ends the game. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. a9e72a95fb991b06d79eaf44b61835a529800b60 Gamehelppuertorico 0 23 80 49 2012-03-11T14:46:48Z Een 3 wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game by collecting: * victory points earned for buildings * victory point tokens earned at the captain phase * conditional victory points earned at the end of the game for occupied large buildings In case of a tie, the player owning the most doubloons and goods is the winner. == Rules summary == The game is played over several rounds. At the start of a round, the governor selects a role, then each player in turn selects a role. At the end of the round, the governor token moves to the next player. When a role has been chosen, each player in turn plays the action for this role. Only the player who selected the role can use the role privilege. Available roles are the following: * Mayor: colonists set foot in the new world ! Privilege : the mayor can get one more colonist from the supply. Action : in turn, begining with the mayor, players get one colonist from the ship, until it is empty. Then players have to set colonists on their buildings and plantations. * Craftsman: goods are produced. Privilege : the craftsman can produce one more good of his choice. Action : produce goods for your occupied buildings and plantations. * Trader: goods are sold to the trading house. Privilege : the trader earns 1 extra doubloon if he sells. Action : sell one good the trading house doesn't have yet. * Settler : plantations settlement. Privilege : the settler can get a quarry instead of a plantation. Action : get a plantation chosen among those available. * Builder : buildings are bought and built. Privilege : the builder can get a building for 1 doubloon less than the regular price. Action : get a building chosen among those available. * Captain : victory points are won by shipping goods to the old world. Privilege : the captain earns one extra victory points if he ships some goods. Action : select a type of good to load on a cargo ship; 1 victory point is earned for each barrel of this type of good loaded on the ship. * Prospector : gold rush brings money ! Privilege : the prospector gets one doubloon from the bank. Action : none. Please also note that occupied violet buildings have modifier effects for role actions. End of game happens at the end of the round for which one of the following events comes to pass : * there is not enough colonists in the supply to refill the colonist ship at the end of the mayor phase * the last victory point token has been earned during the captain phase * at least one of the players built on his 12th and last free space in the city. == Playing online == Choosing a role : click on the role of your choice in the list on the right of your game board. * Mayor role : accept or refuse to get an extra colonist from the supply. Colonists are then automatically recruited by players. Click on a building or a plantation to setup a colonist on it. Click on a colonist to send it back to San Juan (the counter for colonists in San Juan is situated in your player panel on the right of the page). * Craftsman role : goods production is automated (counters for goods owned are in the player panel on the right of the page). Then, choose one extra good that you can produce as your privilege. * Trader role : choose a good that you want to sell by clicking on the appropriate counter in your player panel. * Settler role : click on the plantation (or quarry) that you want. Placement on your board is automated. * Builder role : click on the building that you want to buy. Placement on your board is automated. * Captain role : click on the cargo boat on which you want to ship some goods. Then choose the type of good that you want to ship by clicking on the appropriate counter in your player panel. At the end of the captain phase, if you have some goods to store (warehouse or windrose), click on the corresponding counter. Stored goods will be highlighted by a red frame. * Prospector role : doubloon is earned immediately. Tooltips are available to give information on roles and buildings by hovering over game elements with the mouse. == 'Balanced game' variant == This variant implements two game balance fixes giving each player an equal chance of winning as listed in the [http://en.wikipedia.org/wiki/Puerto_Rico_%28board_game%29 Puerto Rico (board game) Wikipedia page]. * The prices of the Factory and University buildings are swapped so that the Factory costs 8 doubloons and the University costs 7 doubloons * Any player that starts with a corn plantation starts with 1 doubloon less than the players that start with an indigo plantation. '''Have a good game !''' 0b804619fb7d51434ad55e5ff5672a4cd2fdbba9 Gamehelpsaboteur 0 32 82 2012-03-11T18:05:12Z Een 3 Created page with " == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with othe..." wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other gold diggers, prevent the gold diggers to get to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! '''Have a good game !''' a4923cb01ed4c3387797dd7d0f2a746908e8ed39 83 82 2012-03-11T18:31:04Z Een 3 /* Available variants */ wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other gold diggers, prevent the gold diggers to get to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) '''Have a good game !''' 89f9a450f60ec687725cea1fef28dabb34a1dc9b 84 83 2012-03-25T17:25:23Z Coyotek4 143 /* Goal */ wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other saboteurs, prevent the gold diggers to get to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) '''Have a good game !''' aa60b7d77920dffe16a09bc974a9b8de56a45063 85 84 2012-03-25T17:26:37Z Coyotek4 143 /* Goal */ wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other saboteurs, prevent the gold diggers from getting to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) '''Have a good game !''' ce5786063d5cbea6ae574ca2e25238661377ad54 Gamehelpcoloretto 0 33 86 2012-03-29T15:09:59Z Greywolf 379 Jokers Rule wikitext text/x-wiki In the regular game, one gets to determine where to place the jokers but apparently for this online version, the jokers are auto-allocated to your highest stacks. bb7190f77b09c6cf389d7b52c1a18a36bea5403a Referral 0 11 88 29 2012-04-03T07:31:54Z Toeeatingdog 468 /* How does this work? */ wikitext text/x-wiki [[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player creates an account on Board Game Arena from your referral web address(see below), he becomes one of your '''referees'''. As soon as this referee playes 3 games, you become a member of BGA club. == How to get referees? == To recruit referees, use your personal referrer web address: {sponsorshipurl} New players MUST create an account from this web address to become one of your referees. == How to get many referees? == * [http://boardgamearena.com/#!doc/sponsor?facebook Tell my friends on Facebook] * [http://boardgamearena.com/#!doc/sponsor?twitter Tell my followers on Twitter] * [http://boardgamearena.com/#!doc/sponsor?email Tell my friends by email] * Publish my personal web address in some forum, send it by MSN, ... == How many BGA club membership days can I win? == As soon as your first referee finish his '''third''' game on Board Game Arena, you win a '''1 month''' BGA club membership. ''Note: your account upgrade will be effective within 24 hours.'' As soon as you get '''2 referees''' with at least '''4 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. As soon as you get '''3 referees''' with at least '''5 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. ... and so on: as your referees number grows you can win many free memberships ! == Referees and multiple account == It is of course forbidden to create fake referee accounts to win free memberships. 9099f9116dd4534c68393a2381c254cc0c2afa8a 89 88 2012-04-03T07:32:27Z Toeeatingdog 468 /* How does this work? */ wikitext text/x-wiki [[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player creates an account on Board Game Arena from your referrer web address (see below), he becomes one of your '''referees'''. As soon as this referee playes 3 games, you become a member of BGA club. == How to get referees? == To recruit referees, use your personal referrer web address: {sponsorshipurl} New players MUST create an account from this web address to become one of your referees. == How to get many referees? == * [http://boardgamearena.com/#!doc/sponsor?facebook Tell my friends on Facebook] * [http://boardgamearena.com/#!doc/sponsor?twitter Tell my followers on Twitter] * [http://boardgamearena.com/#!doc/sponsor?email Tell my friends by email] * Publish my personal web address in some forum, send it by MSN, ... == How many BGA club membership days can I win? == As soon as your first referee finish his '''third''' game on Board Game Arena, you win a '''1 month''' BGA club membership. ''Note: your account upgrade will be effective within 24 hours.'' As soon as you get '''2 referees''' with at least '''4 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. As soon as you get '''3 referees''' with at least '''5 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. ... and so on: as your referees number grows you can win many free memberships ! == Referees and multiple account == It is of course forbidden to create fake referee accounts to win free memberships. 63171eacf8f3dcb07c87ea7f365a438d800754c9 90 89 2012-04-03T07:33:42Z Toeeatingdog 468 /* Referees and multiple account */ wikitext text/x-wiki [[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player creates an account on Board Game Arena from your referrer web address (see below), he becomes one of your '''referees'''. As soon as this referee playes 3 games, you become a member of BGA club. == How to get referees? == To recruit referees, use your personal referrer web address: {sponsorshipurl} New players MUST create an account from this web address to become one of your referees. == How to get many referees? == * [http://boardgamearena.com/#!doc/sponsor?facebook Tell my friends on Facebook] * [http://boardgamearena.com/#!doc/sponsor?twitter Tell my followers on Twitter] * [http://boardgamearena.com/#!doc/sponsor?email Tell my friends by email] * Publish my personal web address in some forum, send it by MSN, ... == How many BGA club membership days can I win? == As soon as your first referee finish his '''third''' game on Board Game Arena, you win a '''1 month''' BGA club membership. ''Note: your account upgrade will be effective within 24 hours.'' As soon as you get '''2 referees''' with at least '''4 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. As soon as you get '''3 referees''' with at least '''5 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. ... and so on: as your referees number grows you can win many free memberships ! == Referees and multiple accounts == It is of course forbidden to create fake referee accounts to win free memberships. aae72040c79a6b18ef8bd1486cd56f5f97eb30d7 Gamehelphawaii 0 34 91 2012-04-04T08:57:29Z Tobylongbeach 477 Created page with "Overview: You are the chieftain of a kingdom (your placemat) with up to five villages (rows). You buy or go to the islands to get place tiles that you put in your villages. Th..." wikitext text/x-wiki Overview: You are the chieftain of a kingdom (your placemat) with up to five villages (rows). You buy or go to the islands to get place tiles that you put in your villages. There are five rounds and you get a decreasing amount of resources each round with which to acquire place tiles. Its a many-paths-to-victory kind of game. There are three kinds of resources: 1) Feet - let you move around the board, or travel to the islands or go to the cove to catch fish. 2) Clam shells - let you buy place tiles from the board 3) Fruit - can be used in place of feet or clam shells in all-or-nothing fashion. If you substitute for feet, you must substitute the entire moving cost. If you substitute for clams, you must substitute the entire purchase price. Moving: Your chieftain start at the beach and it costs nothing to return there from the board. Each movement in from the beach costs one foot. Moving up, down, right, left or diagonal on the board costs one foot per space. Staying on a space costs one foot. To go to the islands, you use your boats and feet. The closest island costs three feet and the farthest costs six. You have to have the feet as well as the space on the boats for the feet. You get an immediate victory point bonus for going to the islands but you do not get a price token (more about that later). Also, you get the level 2 version of whatever place tile or item you are getting on the islands. For instance, to go to the six foot island, you would need either: A) two three-feet boats and six feet resources or B) you would need the four-foot boat (which comes with a prepaid foot) and the two-foot boat as well as five feet resources. That is the advantage of the pre-paid foot on the four-foot boat. == Purchasing: == To buy some thing from the board: Select the price token (round colored circle with number inside) on the space with the place tile or item you want. If there is more than one place tile available on that space, you will be given a choice of which one you want. The movement cost will be in feet and the purchase price will be in clam shells but you can substitute either or both with fruit if you have it. You gain the price token for the round. There must be an available price token in order to purchase something from that space. You can pay double to get the level 2 version, which for most tiles is double effect. You place it in the village (row) of your choice. You must be able to place it in order to purchase it. A) You can only have one of each type of place tile in each village. For instance, you cannot have a foot (1) hut and a foot (2) hut in the same village but you can have a coconut fruit (1) tree and a breadfruit (2) tree in the same village. B) You can have only one god in each village. You can only have one of each type of god in your whole kingdom and the god vp effects are kingdom-wide. B) Each village must begin with a hut. The huts have a picture of a hut on them: foot hut, clam hut, spear hut, exchange hut, and long hut. ''' == Resources == ''' == Base reources == Base resources are on the round indicator. They decrease with each succeeding round. Everyone gets the base resources at the beginning of the round. Any unused resources can be used in the next round. == In-play resource bonuses == *The third, fourth and fifth villages give you 1, 2 and 3 clams respectively when you purchase Kahunas (Hawaiian priests) for them. *When you purchase for your kingdom the second, third and fourth Tikis, you are granted 1, 2, and 3 feet, respectively. *One or two of the island treasures are four fruit resources. == Resource Generators: == generate resources at the beginning of the next round. Fruit level ones - one fruit Fruit level twos - two fruit Clam hut level one - one clam Clam hut level two - two clams Foot hut level one - one foot Foot hut level two - two feet Irrigation level two - one resource of your choice Ku god - level one and two - one foot Kane god level one - one clam Kane god level two - two clams '''== Victory Points ==''' == In-play VP's: == *Going to the islands gives you a vp bonus. *Some of the treasures on the islands are 5 vp's. *Spear huts and the Ku god give you a bonus whenever you take a price token with crossed spears. Level 1 is 1 pt. bonus, Level 2 is two pt. bonus. == Round-End VP's: == There is a large sheild on the round indicator at the bottom. If the sum of price tokens and fish are greater than the number on the sheild, then you get a bonus. There is a large bonus for the player with highest sum score, a moderate bonus for the second highest and a token bonus for everyone else whose sum was greater than the sheild. Surfer place tiles lower the sheild value for qualifying for the bonus but surfers do not contribute to your sum when comparing to other players. Lono god level 1 gives you 2 vp's if you qualify for the bonus, and Lono god level 2 gives you 4 vp's if you qualify for the bonus == Game-End VP's: == A) Each village whose size extends to the Tikis and which possesses a Kahuna is active and receives a bonus, listed next to the Kahunas. The first, second, third, fourth and fifth villages receive five, five, ten, ten and fifteen points respectively. Only active villages score points for place tiles in the village. B) Hula dancer gives you one point for each place tile in her village including herself. Level 2 hula dancer gives you two points instead of one. C) Irrigation gives you pts for each type of fruit you have in that particular village. D) Long Hut level 2 is worth five points. E) Laka god scores one pt for each fruit symbol on your place tiles. Laka god level 2 scores two points for each fruit symbol. F) Kanaloa god scores two pts for each boat and surfer. Kanaloa 2 scores 4 pts for each boat and surfer. c7659be503bee1cb94d917358ea72ccc219c822d Gamehelpcantstop 0 35 92 2012-04-16T17:11:49Z Iakwvina 538 Created page with "Can't Stop is a very easy and fast game! '''How to play''' You roll 4 dices and then you decide which summaries of 2 dices you want to use.When you do that,you place the tem..." wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' You roll 4 dices and then you decide which summaries of 2 dices you want to use.When you do that,you place the temporary markers on the game board.You are allowed to use 3 temporary markers.These markers are commonly white or black.You continue to roll the dices hoping to bring one of the previous combinations.When you think that it is enough you must say "STOP". Then you replace the white markers with yours.If you roll the dices and there is not possible combination you fail and your progress doesn't be saved.The game board has paths from 2 to 12.The most common are 6,7 and 8 so they have more steps in their paths. '''The object of the game is''' to reach and cover 3 peeks of these paths! 1c67b4df40a8303ccc8214a7e52670b1f7e4ffbe 93 92 2012-04-17T00:45:02Z Jleidich 100 wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' Roll 4 standard dice and then choose the sums of any 2 dice to advance your position on the board. You are allowed to use 3 temporary markers to track your progress in columns marked 2 through 12. These markers are commonly white or black. You can continue to roll the dice hoping to roll one of the previous combinations in the three columns selected this turn. When you think you've pushed your luck far enough you say "STOP" then pass the dice to the next player and replace the white markers with your own. If you roll the dice and find no useful combination you fail and your progress is lost. The more common numbers on 2d6 (6,7 and 8) have more steps in their paths while the less common combinations (2 and 12) have less. '''The object of the game is''' to reach and cover 3 peaks of these columns! a3f0344e922c7148713a1809f6a3e5275412f2f6 Sponsor 0 36 94 2012-04-17T18:31:04Z Een 3 Redirected page to [[Referral]] wikitext text/x-wiki #REDIRECT [[Referral]] ad3d24fb99469ddbdfa9c56714b949f98a1e82b8 Gamehelphawaii 0 34 101 91 2012-05-20T18:07:54Z Tobylongbeach 477 wikitext text/x-wiki Overview: You are the chieftain of a kingdom (your placemat) with up to five villages (rows). You buy or go to the islands to get place tiles that you put in your villages. There are five rounds and you get a decreasing amount of resources each round with which to acquire place tiles. Its a many-paths-to-victory kind of game. There are three kinds of resources: 1) Feet - let you move around the board, or travel to the islands or go to the cove to catch fish. 2) Clam shells - let you buy place tiles from the board 3) Fruit - can be used in place of feet or clam shells in all-or-nothing fashion. If you substitute for feet, you must substitute the entire moving cost. If you substitute for clams, you must substitute the entire purchase price. Moving: Your chieftain start at the beach and it costs nothing to return there from the board. Each movement in from the beach costs one foot. Moving up, down, right, left or diagonal on the board costs one foot per space. Staying on a space costs one foot. To go to the islands, you use your boats and feet. The closest island costs three feet and the farthest costs six. You have to have the feet as well as the space on the boats for the feet. You get an immediate victory point bonus for going to the islands but you do not get a price token (more about that later). Also, you get the level 2 version of whatever place tile or item you are getting on the islands. For instance, to go to the six foot island, you would need either: A) two three-feet boats and six feet resources or B) you would need the four-foot boat (which comes with a prepaid foot) and the two-foot boat as well as five feet resources. That is the advantage of the pre-paid foot on the four-foot boat. == Purchasing: == To buy some thing from the board: Select the price token (round colored circle with number inside) on the space with the place tile or item you want. If there is more than one place tile available on that space, you will be given a choice of which one you want. The movement cost will be in feet and the purchase price will be in clam shells but you can substitute either or both with fruit if you have it. You gain the price token for the round. There must be an available price token in order to purchase something from that space. You can pay double to get the level 2 version, which for most tiles is double effect. You place it in the village (row) of your choice. You must be able to place it in order to purchase it. A) You can only have one of each type of place tile in each village. For instance, you cannot have a foot (1) hut and a foot (2) hut in the same village but you can have a coconut fruit (1) tree and a breadfruit (2) tree in the same village. B) You can have only one god in each village. You can only have one of each type of god in your whole kingdom and the god vp effects are kingdom-wide. B) Each village must begin with a hut. The huts have a picture of a hut on them: foot hut, clam hut, spear hut, exchange hut, and long hut. ''' == Resources == ''' == Base reources == Base resources are on the round indicator. They decrease with each succeeding round. Everyone gets the base resources at the beginning of the round. Any unused resources can be used in the next round. == In-play resource bonuses == *The third, fourth and fifth villages give you 1, 2 and 3 clams respectively when you purchase Kahunas (Hawaiian priests) for them. *When you purchase for your kingdom the second, third and fourth Tikis, you are granted 1, 2, and 3 feet, respectively. *One or two of the island treasures are four fruit resources. == Resource Generators: == generate resources at the beginning of the next round. *Fruit level ones - one fruit *Fruit level twos - two fruit *Clam hut level one - one clam *Clam hut level two - two clams *Foot hut level one - one foot *Foot hut level two - two feet *Irrigation level two - one resource of your choice *Ku god - level one and two - one foot *Kane god level one - one clam *Kane god level two - two clams '''== Victory Points ==''' == In-play VP's: == *Going to the islands gives you a vp bonus. *Some of the treasures on the islands are 5 vp's. *Spear huts and the Ku god give you a bonus whenever you take a price token with crossed spears. Level 1 is 1 pt. bonus, Level 2 is two pt. bonus. == Round-End VP's: == There is a large sheild on the round indicator at the bottom. If the sum of price tokens and fish are greater than the number on the sheild, then you get a bonus. There is a large bonus for the player with highest sum score, a moderate bonus for the second highest and a token bonus for everyone else whose sum was greater than the sheild. Surfer place tiles lower the sheild value for qualifying for the bonus but surfers do not contribute to your sum when comparing to other players. Lono god level 1 gives you 2 vp's if you qualify for the bonus, and Lono god level 2 gives you 4 vp's if you qualify for the bonus == Game-End VP's: == A) Each village whose size extends to the Tikis and which possesses a Kahuna is active and receives a bonus, listed next to the Kahunas. The first, second, third, fourth and fifth villages receive five, five, ten, ten and fifteen points respectively. Only active villages score points for place tiles in the village. B) Hula dancer gives you one point for each place tile in her village including herself. Level 2 hula dancer gives you two points instead of one. C) Irrigation gives you pts for each type of fruit you have in that particular village. D) Long Hut level 2 is worth five points. E) Laka god scores one pt for each fruit symbol on your place tiles. Laka god level 2 scores two points for each fruit symbol. F) Kanaloa god scores two pts for each boat and surfer. Kanaloa 2 scores 4 pts for each boat and surfer. a5b2616a703904b9170cd5ac89d2fbd3cbbf94d5 Rating 0 16 102 72 2012-05-26T13:50:59Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == How is my ELO ranking computed ? == The BGA ELO system is inspired by the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. Let's try to make it simple: the points you win/lose after each game depends on the ranking of your opponents. If you win at a table with opponents stronger than you, your ranking will increase a lot. If you lose at the same table, your ranking will decrease a little. And so on ... In case there are more than 2 players at the table, we take into account the final rank. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO a6dc048df01d400d3a40159030d500d2c6d0eeb7 103 102 2012-05-26T14:00:10Z Sourisdudesert 1 wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is inspired by the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. Let's try to make it simple: the points you win/lose after each game depends on the ranking of your opponents. If you win at a table with opponents stronger than you, your ranking will increase a lot. If you lose at the same table, your ranking will decrease a little. And so on ... In case there are more than 2 players at the table, we take into account the final rank. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 35a2f8521b67c643b02c1cfed37ce9bd61ac058e 104 103 2012-05-26T14:04:17Z Sourisdudesert 1 /* How is my ELO ranking computed ? */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO e52ee9312119ec37ea571332e9f96cf44c69833b 105 104 2012-05-26T14:04:53Z Sourisdudesert 1 /* Some advice */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO e7710d254d0b0589ff5ca25ac485b8c6f3a44bbf 106 105 2012-05-26T14:15:09Z Sourisdudesert 1 /* How is my ELO ranking computed ? */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or loose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you loose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or loose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * Table Level (TL) is the average of ELO ratings of all players at this table. * The success (S) of a player depends directly on its ranking at the game, from S=+470 (winner of the game) to S=-470 (last player). * The performance (P) of a player is TL+S. * The new ELO rating (Rn) is a weighted average of the previous ELO rating (Rp) and the performance: Rn = K.P + (1-K).Rp == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 3cd935f80c6c6f00744f005218d18d42bd5e6acf 107 106 2012-05-26T14:18:41Z Sourisdudesert 1 /* Okay, but I want to know the formula ! */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you loose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or loose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you loose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or loose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == The formula is exactly one's used by the ELO rating system, with the following adjustments: * At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * K=60 for the first 30 games, K=40 afterwards. * K is multiply by (N/2) for N-players games. If N exceed the "advised number of players" for this game, K is multiply by (A/2), where A is the "advised number of players". * If 2 players has a rating difference greater than 600, we consider that their ELO difference is 600. == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 83d9c1282c8bbed7224adc1193fa6025698360b1 Gamehelpcaylus 0 37 108 2012-06-08T12:16:00Z Xeno611 648 a fix wikitext text/x-wiki The link above for the rules text is broken, here is the proper one [http://www.ystari.com/caylus/CayUS.pdf Caylus Rule book] dec8842e16c43a0bdf8306d09cefef9977201436 Gamehelpdiams 0 38 109 2012-06-13T20:56:30Z Een 3 Created page with " == Goal == Get as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color or more (up to five). Each color has a matchi..." wikitext text/x-wiki == Goal == Get as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color or more (up to five). Each color has a matching value between 1 to 10. == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market Then you will have to choose between two actions (phase 2): * Select one of your card and go to the market with this card. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose values sum up to the value of the card you went to the market with ** Come back empty ended * Select cards in your hand and secure them into your safe. Your selection: ** Must contain at least one collection ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some cards to keep only 5. == Special cards == == Available variants == '''Have a good game !''' c548022931a8d0df231a0d4b6e840d226e525204 110 109 2012-06-13T21:06:55Z Een 3 wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. On each round, you'll earn victory chips based on the points earned collecting diamonds. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is the '7', not the '10') The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. Your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == == Available variants == '''Have a good game !''' ceb5509a22f4538c3055f3866cc8ebb9dbac81f4 111 110 2012-06-13T21:21:17Z Een 3 /* Available variants */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. On each round, you'll earn victory chips based on the points earned collecting diamonds. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is the '7', not the '10') The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. Your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 643d9f4bc5d615656496eb5a5ed70d66cf6da703 112 111 2012-06-13T21:31:05Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. On each round, you'll earn victory chips based on the points earned collecting diamonds. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is the '7', not the '10') The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. Your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market ''Secur`express'' enables you to secure diamonds of one quality already present in your safe ''Safe drilling'' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent ''Sleight of hand'' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' d596468bbd4851645c67eb9a7a0cb676984c0ec6 113 112 2012-06-13T21:32:39Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10') On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. Your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market ''Secur`express'' enables you to secure diamonds of one quality already present in your safe ''Safe drilling'' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent ''Sleight of hand'' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 43e2f2bf0e3fbfaa319218cfe03ee62cef32271a 114 113 2012-06-13T21:34:02Z Een 3 /* Rules summary */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10') On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market ''Secur`express'' enables you to secure diamonds of one quality already present in your safe ''Safe drilling'' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent ''Sleight of hand'' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 695fba417b313f914ffd6c149492f05049def263 115 114 2012-06-13T21:34:25Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10') On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 19ceabc9061b0a187671c4db40e214f66516dc65 116 115 2012-06-13T21:34:42Z Een 3 /* Available variants */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10') On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 6131f77032d5baa37874120100912d9bdb56cf75 117 116 2012-06-13T21:37:38Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity in his safe (magnifying glass symbol) and for the player whose diamonds digits in hand sum up to the biggest value. On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' d1b0220c82a08620127520971f823abe8813b2b2 118 117 2012-06-13T21:39:26Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity in his safe (magnifying glass symbol, 5 bonus points, 2 points in case of a tie) and for the player whose diamonds digits in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 868fbcf9a41ea50e24a1a14474160cce69b6733b 119 118 2012-06-13T21:40:34Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other and unique power. Available powers are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 6e0a67b750f9a539b72ac427ef89189b58fd6016 120 119 2012-06-13T21:41:33Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' e04a679791f79449ec50aa30c74413cf1e5df788 121 120 2012-06-13T21:42:01Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds. The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 5ac6a2422de77758d42de4dc6cb285e50c1d730e 122 121 2012-06-13T21:45:22Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 and so on). The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' e8f0cd0e229a5b47857ebd7b85a02ae1fabc94d4 123 122 2012-06-13T21:45:39Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many diamond collections as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the most victory chips at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 9cfe856cf374159dc625af28fbb99068cc5d0266 124 123 2012-06-13T21:46:29Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two possibilities (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two actions (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 1f43bf7cba2bf5a15119b37f4e4ca3b63e58f395 125 124 2012-06-13T21:47:30Z Een 3 /* Rules summary */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game !''' 8dbb958af624cfa3ddb5963303e0f8fbfc902997 126 125 2012-06-15T18:53:45Z Een 3 wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 92496c4b6e26fb4f7c997524ee11bcbd1fdf9cd6 127 126 2012-06-15T18:56:45Z Een 3 wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 39b1f29563cdb81647bdd16d15da8b20442a7718 128 127 2012-06-16T13:46:46Z Een 3 wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 8da68d505b4bab957ec3a8b0c562e040f7ced9dc 129 128 2012-06-18T18:49:28Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching quality digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality, or a diamond of exceptional purity, straight from the safe of an opponent '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 7f051d4fe265413fd7537709dbadfe420396ed1f 130 129 2012-06-18T18:51:57Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching quality digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (please take note however, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 45da0f7bedaf3c9f89202f20840a6bd2d54e5c65 131 130 2012-06-18T18:59:23Z Een 3 /* Goal */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (please take note however, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 43ea17ba2484b5535f8073731b3d0c7a6eeafffd 132 131 2012-06-18T18:59:56Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (please take note however that if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 11ee17814253934e13773d95ad05fd1a98616ea9 133 132 2012-06-18T19:00:33Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (of course, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' f7ae2b1fd802f3d68b779ddba760461388c132dd 134 133 2012-06-18T19:47:14Z Een 3 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power you to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (but of course, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 9a4d8f6b6f72f40fca58395367ce2b205dfd0e11 Gamehelpstoneage 0 21 135 99 2012-06-26T15:37:57Z Bastan 978 /* Game end */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). Special rules for game with 2 players only: * no more than 1 player may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field Special rules for game with 3 players only: * no more than 2 players may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * '''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). * each resource that a player has on his player board scores 1 point a253652bd7e7b7a766518baaba762f793c4fe7f6 Contact us 0 9 136 61 2012-06-27T18:25:25Z Een 3 wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive ''a lot'' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the corresponding forum * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Responsible == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 51933312a01a4748b0ec1631205b3a80418dc06d 137 136 2012-06-27T18:29:32Z Een 3 /* Contact e-mail */ wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive '''a lot''' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the corresponding forum * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Responsible == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 7bf5a90e4b8f025c1ae505fd06cd2eff3a1e50d2 138 137 2012-06-27T18:29:51Z Een 3 /* Responsible */ wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive '''a lot''' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the corresponding forum * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Administrator == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 62315f21725bfc9363f06b56eb43a553eb505eb1 139 138 2012-06-27T18:31:18Z Een 3 /* Administrator */ wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive '''a lot''' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the corresponding forum * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Postal address == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 e608e0a52f031694659de3f162e71fd4c20a7fff Contact 0 39 140 2012-06-27T18:40:51Z Een 3 Redirected page to [[Contact us]] wikitext text/x-wiki #REDIRECT [[Contact us]] 44227f4a379d1cb9c906fc5923f86e642d717086 Aboutus 0 40 141 2012-06-27T18:42:01Z Een 3 Redirected page to [[About us]] wikitext text/x-wiki #REDIRECT [[About us]] bb476bbb3a6b3eb744f44c0fbdfcb920954a6022 Gamehelpraceforthegalaxy 0 41 142 2012-06-28T09:06:10Z Spacediver 718 Created page with "You may drag a resource (single-coloured small card) to a consumption power to consume it." wikitext text/x-wiki You may drag a resource (single-coloured small card) to a consumption power to consume it. f221b6292998d419075a2a637383e0b7808e4b7a 143 142 2012-06-30T06:05:24Z Entropy2k 993 wikitext text/x-wiki In Race for the Galaxy, players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods, and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, consuming goods, and bonuses from special developments. There are 5 phases to choose from: explore, develop, settle, consume, and produce. Players pick 1 phase per turn ( two phases in a 2 player game), then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player plays 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. 332ed8ac115c5b36871360de512d2523c65ceca7 Gamehelpdragonheart 0 31 144 96 2012-07-14T21:55:41Z Crazy c 1053 /* Cards */ wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 2 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards are played ends the game and the opponent takes one final turn. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. cb9673f3d13c9a0f1bbdc1666c6d3f1298f6486f 145 144 2012-07-14T21:57:15Z Crazy c 1053 /* End */ wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 3 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards are played ends the game and the opponent takes one final turn. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. e984bb914251f4a0f157c7a0785d53b3d4905237 146 145 2012-07-14T21:58:29Z Crazy c 1053 /* End */ wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 3 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. In case of a tie, the player with the Great Dragon wins. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are five types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards are played ends the game and the opponent takes one final turn. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. 3e0a5f8d9ad58be87706271bf73b4fcc52471ff6 Gamehelptobago 0 27 147 87 2012-07-19T06:49:54Z Crazy c 1053 /* 2) Move ATV from 1 to 3 legs */ wikitext text/x-wiki == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map indicate where the treasure can be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after the last treasure card has been given out. '''Have a good game !''' 162a27ee6a534e10c342b291885452d9fdf04053 148 147 2012-07-19T07:09:01Z Crazy c 1053 /* 1) Play a clue card from his hand */ wikitext text/x-wiki == Goal == Get as many gold coins as you can by locating treasures on the Tobago island and sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. The player can decide which treasure map he wants to play his clue card on. In order to play a clue card on a map you must make sure that: *The new clue card mustn't contradicted any present clue cards of the specific treasure map. *The new clue card must reduce at least one marker from the current amount of markers of the specific treasure map (if there are markers on the board. If there are no markers on the board, you can still put a clue card even if it doesn't change anything). *The new clue card must keep it possible for at least one marker to stay on the board (a treasure cannot disappear from the board). When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map are placed on the island, indicating where the treasure might be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after the last treasure card has been given out. '''Have a good game !''' e77ddbfe3be6e45d3526a4e0ace96c704f87d2b8 149 148 2012-07-19T07:10:35Z Crazy c 1053 /* Goal */ wikitext text/x-wiki == Goal == Be the player with the most gold coins by locating treasures on the Tobago island and/or sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. The player can decide which treasure map he wants to play his clue card on. In order to play a clue card on a map you must make sure that: *The new clue card mustn't contradicted any present clue cards of the specific treasure map. *The new clue card must reduce at least one marker from the current amount of markers of the specific treasure map (if there are markers on the board. If there are no markers on the board, you can still put a clue card even if it doesn't change anything). *The new clue card must keep it possible for at least one marker to stay on the board (a treasure cannot disappear from the board). When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map are placed on the island, indicating where the treasure might be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. When a treasure is raised, the current move is finished, even if the player has not used all the legs to which he was entitled. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! The game ends after the last treasure card has been given out. '''Have a good game !''' 1349f4ae9b3bc89321b0a00622234f29a6611878 150 149 2012-07-19T07:22:16Z Crazy c 1053 /* 2) Move ATV from 1 to 3 legs */ wikitext text/x-wiki == Goal == Be the player with the most gold coins by locating treasures on the Tobago island and/or sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. The player can decide which treasure map he wants to play his clue card on. In order to play a clue card on a map you must make sure that: *The new clue card mustn't contradicted any present clue cards of the specific treasure map. *The new clue card must reduce at least one marker from the current amount of markers of the specific treasure map (if there are markers on the board. If there are no markers on the board, you can still put a clue card even if it doesn't change anything). *The new clue card must keep it possible for at least one marker to stay on the board (a treasure cannot disappear from the board). When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map are placed on the island, indicating where the treasure might be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. ===3) Treasure sharing and amulets=== When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. Raising a treasure will end a player's turn, even if the player has not used all 3 legs. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! ===End of the game=== The game ends after the last treasure card has been given out. '''Have a good game !''' 3006885193bc285203a8f1e9db8ee4f4e3a7c323 Gamehelptobago 0 27 151 150 2012-07-19T07:28:59Z Crazy c 1053 /* End of the game */ wikitext text/x-wiki == Goal == Be the player with the most gold coins by locating treasures on the Tobago island and/or sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. The player can decide which treasure map he wants to play his clue card on. In order to play a clue card on a map you must make sure that: *The new clue card mustn't contradicted any present clue cards of the specific treasure map. *The new clue card must reduce at least one marker from the current amount of markers of the specific treasure map (if there are markers on the board. If there are no markers on the board, you can still put a clue card even if it doesn't change anything). *The new clue card must keep it possible for at least one marker to stay on the board (a treasure cannot disappear from the board). When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map are placed on the island, indicating where the treasure might be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. ===3) Treasure sharing and amulets=== When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. Raising a treasure will end a player's turn, even if the player has not used all 3 legs. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! ===End of the game=== The game ends when the treasure card pile runs out. '''Have a good game !''' dae010445287c763c8ab4bb06d1c09883e19707b 161 151 2012-08-27T23:39:45Z Villager 1295 /* 3) Treasure sharing and amulets */ wikitext text/x-wiki == Goal == Be the player with the most gold coins by locating treasures on the Tobago island and/or sharing them with other players. == Rules summary == On their turn, each player can choose one of two actions: ===1) Play a clue card from his hand=== The clue cards give the positioning of a treasure for a map of a given color relative to a land type (jungle, mountains, beach, scrubland, river, lake or sea) or to a landmark (palm tree, hut or statue). Positioning clues are as follows: in, next to, in sight of, not in, not next to, not in sight of. When the position is given relative to a land type, if there are red brackets above and below the land type it means that the land to use for this clue is the largest of this type. The player can decide which treasure map he wants to play his clue card on. In order to play a clue card on a map you must make sure that: *The new clue card mustn't contradicted any present clue cards of the specific treasure map. *The new clue card must reduce at least one marker from the current amount of markers of the specific treasure map (if there are markers on the board. If there are no markers on the board, you can still put a clue card even if it doesn't change anything). *The new clue card must keep it possible for at least one marker to stay on the board (a treasure cannot disappear from the board). When less than 17 spaces on the island match the clues for a treasure map, markers of the color of the map are placed on the island, indicating where the treasure might be. When there is only one of these markers left, the treasure is located and can be raised. ===2) Move ATV from 1 to 3 legs=== A leg is a move of: * either as many spaces as you want in the same land type * or only one space when moving from one land type to another. ===3) Treasure sharing and amulets=== When a player's ATV is on an island space containing an amulet, the player can pick it up. If the ATV is on a space containing a treasure that has been located, he can raise it, and then players have to share it. Raising a treasure will end a player's turn, even if the player has not used all 3 legs. Sharing of the treasure happens solely between the players who took part in its discovery, either by playing clue cards on the treasure map, or by raising the treasure. Each player is presented with as many treasure cards as they have windroses of their color representing their participation in the discovery of the treasure (a windrose is placed on each clue card that is played, and a windrose is also placed under the last clue card of the map for the player who raises the treasure). The other players do not see these cards. An additional card is taken from the deck and the cards are shuffled. Each player in turn, in the reverse order of placement of windroses on the treasure map, draws a card amidst those belonging to this treasure and chooses either to take it and withdraw his windrose from the map, or to give it to the next player (in the order set by the windroses as described before) who is then presented with the same choice. If no player accepts it, the treasure card is discarded. Two curse cards can be found among the treasure cards. If a curse card is drawn, treasure sharing is over, and all players who are still involved in sharing the treasure (ie players with windroses on the map for the treasure being shared) can either protect themselves by sacrificing an amulet, or lose their most valuable treasure card. When the treasure sharing is over, the last player to have been granted a treasure card must reopen the treasure hunt by playing one clue card for the now empty treasure map. Then, mysterious amulets appear where the gaze of statues strikes the coast, and statues rotate on their base. Once collected, an amulet can be spent at any time during a player's turn to do one of 4 possible actions: * play a clue card * remove a treasure marker * move his ATV three legs (NB: it is not possible to collect other amulets while moving thanks to an amulet.. BUT you can RAISE a treasure!) * to exchange the clue cards from his hand with clue cards from the deck Proper use of amulet power is critical to ensure victory! ===End of the game=== The game ends when the treasure card pile runs out. '''Have a good game !''' 953052c0da61d4fc18048a92604e5b53e2e54e84 Gamehelpdiams 0 38 152 134 2012-07-22T09:03:02Z Crazy c 1053 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will loose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (but of course, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 33228376d9a780183c2c7c45fc52dae361572e2d 153 152 2012-07-22T09:04:15Z Crazy c 1053 /* Special cards */ wikitext text/x-wiki == Goal == Secure as many '''diamond collections''' as you can in your safe! A collection is a set of three diamonds of the same color, or more (up to five). Each color has a matching 'quality' digit ranging from 1 to 10. Collections are worth more or less depending upon the diamond color and the number of diamonds (be careful to check the collection values, the most valuable diamond is '7', not '10'). Bonus points will be earned at the end of the round : * for the player with the most diamonds of exceptional purity (magnifying glass symbol) in his safe (5 bonus points, 2 points in case of a tie) * and for the player whose digits for the diamonds still in hand sum up to the biggest value (3 bonus points, tie or no tie). On each round, you'll earn victory chips based on the points earned collecting diamonds (the 1st player gets 4 chips, the 2d gets 3 chips and so on). The player with the '''most victory chips''' at the end of three rounds wins! (in case of a tie, it's the player who won the 2d round who will get the victory) == Rules summary == On your turn, you must first choose between two 'draw actions' (phase 1): * Draw one card and put it in your hand * Draw two cards and put them on the market. Then you will have to choose between two 'play actions' (phase 2): * Select one of your cards and go to the market with it. Once on the market, you can: ** Take all cards of the same color than the one you went to the market with (including it) ** Take a selection of cards whose digits sum up to the digit of the card you went to the market with ** Or come back empty ended... * Select cards in your hand and secure them into your safe. In order to be valid, your selection: ** Must contain at least one collection (three diamonds of the same color) ** Can contain diamonds matching a collection already present in your safe (in order to expand the collection) At the end of your turn, if you have more than 5 cards in your hand, you will have to discard some to keep only 5. == Special cards == Three special cards give you the power to play tricks on your opponents '''instead of playing phase 2'''. Each of these cards can be used either with the 'Compulsory sale' power that they all share, or with their other (and unique) power. Power effects are as follows: '''Compulsory sale''' enables you to get all diamonds of a quality that you have in your hand from the market ''after'' forcing an opponent to put all his diamonds of this quality on the market (but be careful! If he doesn't have any, you will lose yours!) '''Secur`express''' enables you to secure diamonds of one quality already present in your safe '''Safe drilling''' enables you to get a diamond of a given quality and purity, straight from the safe of an opponent (but of course, if your opponent doesn't have any diamond of this kind, drilling will fail!) '''Sleight of hand''' enables you to take any diamond you want from the market, just like that! == Available variants == '''Game length''': the 'Standard' game is played in 3 rounds. The 'Quick game' variant makes it possible to play a game in just one round when you are in a hurry to get lots of pretty diamonds to pay back your Can't stop gambling debts! '''Closing card''': in the 'Standard' game, the closing card is randomly shuffled among the last 15 cards at 2/3 players, and among the last 5 cards at 4/5 players. With the 'Deterministic' variant, the closing card is the last one, so you can try to outthink your opponents till the very end! '''Have a good game!''' 1129c9ce9f9cc7e7d942da196bfc69693fd04ca7 Gamehelpunitedsquare 0 42 154 2012-07-22T09:46:01Z Crazy c 1053 Created page with "===goal=== The goal is to be the player who has the most squares of his colors. ===rules=== The game contains multicolored squares (red, blue. yellow and green) which appear ..." wikitext text/x-wiki ===goal=== The goal is to be the player who has the most squares of his colors. ===rules=== The game contains multicolored squares (red, blue. yellow and green) which appear as 4 colored triangles on the square. Each player on his turn puts a multicolored square next to a square already on the board. The played square (which can be shifted to fit as requested, keeping the order of the colors the same) must be put in a way that creates at least on colored square (not necessarily your color). If it is not possible for the player to put down a square, the turn is passed to the next player. It is also possible to have "non playable" squares which are created when 2 of the same color are connected to the same open square. This is also a strategy to prevent from your opponent from getting more squares of his color. (e.g. if there is a red triangle facing an empty square and i connect to that same empty square another red triangle from another side, the empty square cannot be played and all colors around the "non playable" square will not become squares and won't gain points). ===ending the gsme=== When all playable squares have been played, the player with the most squares of his color wins the game. HAVE A GOOD GAME! 69c010a69b9893a52e587fba072f34ad0c9fbb12 155 154 2012-07-22T09:46:59Z Crazy c 1053 /* ending the gsme */ wikitext text/x-wiki ===goal=== The goal is to be the player who has the most squares of his colors. ===rules=== The game contains multicolored squares (red, blue. yellow and green) which appear as 4 colored triangles on the square. Each player on his turn puts a multicolored square next to a square already on the board. The played square (which can be shifted to fit as requested, keeping the order of the colors the same) must be put in a way that creates at least on colored square (not necessarily your color). If it is not possible for the player to put down a square, the turn is passed to the next player. It is also possible to have "non playable" squares which are created when 2 of the same color are connected to the same open square. This is also a strategy to prevent from your opponent from getting more squares of his color. (e.g. if there is a red triangle facing an empty square and i connect to that same empty square another red triangle from another side, the empty square cannot be played and all colors around the "non playable" square will not become squares and won't gain points). ===ending the game=== When all playable squares have been played, the player with the most squares of his color wins the game. HAVE A GOOD GAME! fc984f3d37b1db341a2475115260c766cf2def67 Gamehelpseasons 0 43 156 2012-08-13T15:53:58Z Jolelaw 1170 Game help for Season Board Game wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be return to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each card and the crystal owned. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. Card summon by other card but due to limitation of the star will cause the new card to be discarded, no refund. ad8bf0ef8a56d186cb4e1a72485c86c9a94fb6ca 157 156 2012-08-13T15:55:10Z Jolelaw 1170 wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be return to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each card and the crystal owned. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. Card summon by other card but due to limitation of the star will cause the new card to be discarded, no refund. 52bce609540fca265df665f782174a6e635e6c51 185 157 2012-09-21T07:54:53Z Talia 1349 Appending description of the Transmute +1 bonus action. wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be return to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each card and the crystal owned. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. Card summon by other card but due to limitation of the star will cause the new card to be discarded, no refund. aad2a6045db9fb9588409b7c8abd0a0a8e134f43 About us 0 6 158 27 2012-08-15T04:17:28Z Suds307 1213 wikitext text/x-wiki First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic or social constraints. We would like to provide a new game experience with the best of two worlds: video games and board games. Although we aim to provide high quality online games, we are still convinced that the best way to enjoy board games is around a *real* table with friends. This is one of the reason we strongly recommend you to buy physical copies of the games you discover on BGA. The other reason is simple to understand: each game sale is also a revenue for an author and an editor (... we've come full circle). [[Contact us]] 3df742bb9998193b9edd78eb592b6c63e09eb5fd Translation guidelines 0 18 159 68 2012-08-19T18:58:42Z Rokras 1237 /* I fixed some translations, but they don't appear to have changed on the site */ wikitext text/x-wiki The [http://en.boardgamearena.com/#!translationhq collaborative translation system] is meant to make it possible to translate Board Game Arena into any language, in order to enable more people to discover and play board games, even when not knowing English or French. For example, if you want to play 'Dragonheart' with your 10-year-old nephew and he doesn't speak English yet, no problem! Just help us translate Board Game Arena into your language! == What can be translated? == Every string of text for the main site interface and for the games' interfaces are 'internationalized' and can be translated. For now, forum posts and articles of the website (such as this one), cannot be translated. Maybe sometime later... == Who should translate? == This is important: only translate text into a given language if you are a native speaker of the language. When translating a game, the translator should know the game thoroughly and if possible have a box and rulebook of the game in the destination language to check for consistency. Translators should take into account the level of language and the formal/informal pronominal rules usual for the gaming audience of their country. As Board Game Arena's team's mother tongue is French, we will take charge of the French translations. We will also release the first version of the English translation. As we are not perfect speakers of Shakespeare's language, these translations will be open for review and correction by native English speakers under the same conditions as the other languages. == About context == Some strings can be translated differently depending upon the context. When this is the case, you should use the most obvious translation. Then when the site has been released in your language, native speakers will spot incorrect forms (if any) while playing and be able to correct them. Also, you can ask us for context in the [http://forum.boardgamearena.com/viewforum.php?f=11 translation forum] (in English or in French), and we will try to look it up and provide the information. == When will the site be made available in a new language it is being translated into? == As soon as a sufficient number of the strings have been translated in the new language in order for the site to make sense into that language, the Board Game Area team will make the site available in the language. == I fixed some translations, but they don't appear to have changed on the site == Translation files are updated nightly, so you just have to wait till the tomorrow for your changes to appear on the site. == How long will it be possible to change the translations? == The first translations may not be perfect (as explained in the 'about context' section). So they can be modified until they are 'validated'. Validation occurs when a translation has not been modified for 30 days straight. It is then considered stable and valid (golden icon) and cannot be changed anymore. == Is there some reward for translating? == Yes! Every translator that gets 100 translations validated (golden icon) will get one free month of 'Board Game Arena Club' membership (and one more month for every batch of 100 new translations validated). This is of course only a symbolic gesture of thanks for people participating in making BGA accessible to more and more people. The possibility to enjoy board games with people from many countries is the greatest reward of all! == Translation tips == * Use the 'TAB' key to go from one text box to another. This is easier than clicking. * Strings such as ${SOMETHING}, %SOMETHING, <SOMETHING> are markup strings for formatting or substituting text and must be left as is. Their position in the overall string can be changed as appropriate in a given language (for example for pronouns such as ${you}). * When in doubt, leave a comment for the next translator. * When you fix someone's translation, leave a comment if the reason for the change is not obvious. * When the translation can depend on context which is not explicit, go with the most straightforward translation. If it is not correct, it will be spotted and fixed later by native speakers while playing. 39afad37613aa3eedd26702970c7420d0a87fc44 Reputation 0 17 160 73 2012-08-27T02:15:45Z Villager 1295 /* How to increase my reputation / what makes my reputation decrease ? */ wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. Qn: its unclear if the "> 10% unfavorable" warning is calculated against the number of "thumbs up / thumbs down".. or "total number of unique players played with / thumbs down". Hope the formula is the latter.. as the first formula will raise many more warnings.. more idiots give thumbs down, and are lazier to give thumb up. == Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite ! Say at least "hello" and "good luck" at the beginning of the game. "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Stay calm in any circumstances: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happened...), then say you are sorry and leave the game by your own to save your opponents time. * Be a good loser: if your lose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a http://fr.boardgamearena.com/theme/img/common/reputation_down.png for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. d9004c71be8151f6e6ca1166b60f3f1e209e452c 165 160 2012-09-08T18:11:50Z Complete muppet 1365 /* Advices: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png */ wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. Qn: its unclear if the "> 10% unfavorable" warning is calculated against the number of "thumbs up / thumbs down".. or "total number of unique players played with / thumbs down". Hope the formula is the latter.. as the first formula will raise many more warnings.. more idiots give thumbs down, and are lazier to give thumb up. == Advice: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite! Say at least "hello" and "good luck" at the beginning of the game, and maybe "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Always stay calm: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happen...), then say you are sorry and leave the game (don't wait to get expelled) to save your opponents time. * Be a good loser: if your lose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a http://fr.boardgamearena.com/theme/img/common/reputation_down.png for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. 7dd2d7d9b84788c2193110bb923f60b6b20dec5b Game clock 0 15 162 34 2012-09-08T18:06:25Z Complete muppet 1365 /* Time to think */ wikitext text/x-wiki [[Category:Help]] On '''Board Game Arena''' you are playing "live" (real time). Then you has a delay to play your moves. Your initial delay at the beginning of a game is most of the time '''3 minutes'''. At each turn, or on specific occasion, you get an additional delay. If you have no more time to play, you will get a penalty and can become eligible to be expelled from the game. == Time to think == Your alloted time to think is displayed on the right of your player's name. When it's your turn to play. This time is also displayed at the top of the web page. == Game speed == Table administrator can make a choice between 4 game speed profile: * Fast * Normal * Slow * Without time limit The additional amount of time credited each turn depends on chosen game speed profile. Be careful to check game speed before game start to adapt your timing. Note: playing without time limit is strongly discouraged, except if you are playing with friends or to discover a new game. Remember that without time limit, you can't expel a player that stops playing. == Running out of time == As soon as you run out of time, you get a clock penalty. If you goes over your alloted time by more than 3/4/5 minutes (depending on game speed), any opponent can expel you from the game. Of course, it is strongly advised not to run out of time ... == Move time limit == In addition to your classical time to think, you have a limited time alloted for each move. When it's your turn to play, a red bar appears on top of the web page. It symbolized the remaining time limit for this move. The amount of time depends on game speed. If you goes over this limit, you become eligible to be expelled from the game. == "My opponent is too slow" == Each of us has different expectations on game speed. Please remember that as soon as a table has been set up at a given game speed, each player is allowed to use all his alloted time to think. If you want to play fast, set up or join table with "fast" mode only, but don't force an opponent to play when he has the right to do so. == "I would like to think a little" == If you are in a critical step of the game and want to take some time to think, you can click on the link "I would like to think a little" on the top right of the page. Thus, your opponents will receive a message and won't be thinking you are away from keyboard. Clicking on this link is not mandatory, but we encourage its use for courtesy. 5d8716bd998afb9592ab1dc42cf952b41d0cb657 163 162 2012-09-08T18:06:47Z Complete muppet 1365 /* Time to think */ wikitext text/x-wiki [[Category:Help]] On '''Board Game Arena''' you are playing "live" (real time). Then you has a delay to play your moves. Your initial delay at the beginning of a game is most of the time '''3 minutes'''. At each turn, or on specific occasion, you get an additional delay. If you have no more time to play, you will get a penalty and can become eligible to be expelled from the game. == Time to think == Your alloted time to think is displayed on the right of your player's name. When it's your turn to play, this time is also displayed at the top of the web page. == Game speed == Table administrator can make a choice between 4 game speed profile: * Fast * Normal * Slow * Without time limit The additional amount of time credited each turn depends on chosen game speed profile. Be careful to check game speed before game start to adapt your timing. Note: playing without time limit is strongly discouraged, except if you are playing with friends or to discover a new game. Remember that without time limit, you can't expel a player that stops playing. == Running out of time == As soon as you run out of time, you get a clock penalty. If you goes over your alloted time by more than 3/4/5 minutes (depending on game speed), any opponent can expel you from the game. Of course, it is strongly advised not to run out of time ... == Move time limit == In addition to your classical time to think, you have a limited time alloted for each move. When it's your turn to play, a red bar appears on top of the web page. It symbolized the remaining time limit for this move. The amount of time depends on game speed. If you goes over this limit, you become eligible to be expelled from the game. == "My opponent is too slow" == Each of us has different expectations on game speed. Please remember that as soon as a table has been set up at a given game speed, each player is allowed to use all his alloted time to think. If you want to play fast, set up or join table with "fast" mode only, but don't force an opponent to play when he has the right to do so. == "I would like to think a little" == If you are in a critical step of the game and want to take some time to think, you can click on the link "I would like to think a little" on the top right of the page. Thus, your opponents will receive a message and won't be thinking you are away from keyboard. Clicking on this link is not mandatory, but we encourage its use for courtesy. 7995e4abd5d929043908bb375a931bc0fc50a79b 164 163 2012-09-08T18:09:41Z Complete muppet 1365 wikitext text/x-wiki [[Category:Help]] On '''Board Game Arena''' you are playing "live" (real time). That means you have an allotted delay to play your moves. Most of the time, your initial delay at the beginning of a game is '''3 minutes'''. During each turn, or on specific occasions, you get an additional delay. If you have no more time to play, you will get a penalty and can become eligible to be expelled from the game. == Time to think == Your allotted time to think is displayed on the right of your player's name. When it's your turn to play, this time is also displayed at the top of the web page. == Game speed == Table administrator can make a choice between 4 game speed profile: * Fast * Normal * Slow * Without time limit The additional amount of time credited each turn depends on chosen game speed profile. Be careful to check game speed before game start to adapt your timing. Note: playing without time limit is strongly discouraged, except if you are playing with friends or to discover a new game. Remember that without time limit, you can't expel a player that stops playing. == Running out of time == As soon as you run out of time, you get a clock penalty. If you goes over your alloted time by more than 3/4/5 minutes (depending on game speed), any opponent can expel you from the game. Of course, it is strongly advised not to run out of time ... == Move time limit == In addition to your classical time to think, you have a limited time alloted for each move. When it's your turn to play, a red bar appears on top of the web page. It symbolized the remaining time limit for this move. The amount of time depends on game speed. If you go over this limit, you become eligible to be expelled from the game. == "My opponent is too slow" == Each of us has different expectations on game speed. Please remember that as soon as a table has been set up at a given game speed, each player is allowed to use all his allotted time to think. If you want to play fast, set up or join table with "fast" mode only, but don't force an opponent to play when he has the right to do so. == "I would like to think a little" == If you are in a critical step of the game and want to take some time to think, you can click on the link "I would like to think a little" on the top right of the page. Thus, your opponents will receive a message and won't be thinking you are away from keyboard. Clicking on this link is not mandatory, but we encourage its use for courtesy. 8e7b80d9cbd9b3f6552a666e0ca9793092c8f5b3 Gamehelpcolorpop 0 44 166 2012-09-08T18:33:11Z Davejhave 776 Created page with "Select adjacent colored tokens in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens..." wikitext text/x-wiki Select adjacent colored tokens in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as any color. You must pop all adjacent tokens of the same color. 0a4173a50ab5172c145b15e28e7a9496b9078db5 167 166 2012-09-08T18:47:19Z Davejhave 776 wikitext text/x-wiki Select adjacent colored tokens in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as any color. Win instantaly by popping all of your secret colored tokens. You must pop all adjacent tokens of the same color. 17b1fb1d81ac204da6e965e9ebd8afcd310f6572 168 167 2012-09-09T12:32:33Z Een 3 wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva ([http://www.coloradd.net/ ColorADD]). '''Have a good game!''' 3718a603bfcba24969ed526d679dae1232da348f 169 168 2012-09-09T12:35:06Z Een 3 wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva ([http://www.coloradd.net/ ColorADD]). '''Have a good game!''' 4b8ed1d1192e2176b1da8b8aa75ef605b7c580d4 171 169 2012-09-12T19:55:55Z Een 3 wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva ([http://www.coloradd.net/code.asp ColorADD]). '''Have a good game!''' 612b122320e94857a200782521a758ad2c11d445 172 171 2012-09-12T20:00:14Z Een 3 wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. '''Have a good game!''' 34a5c561d3d6540c63bcd99833acc93d4ba5e571 174 172 2012-09-13T17:38:36Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' c1faf3ca99a6d1d9fedff47d69fd8f5b5f3a6f10 175 174 2012-09-13T17:46:08Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] <img alt="Synthesis panel" src="http://en.doc.boardgamearena.com/images/0/0b/ColorADD.jpg" /> '''Have a good game!''' 16f73ad1be3268a2aa7f75f33fc0f84e0fd90b8b 176 175 2012-09-13T17:49:20Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' c1faf3ca99a6d1d9fedff47d69fd8f5b5f3a6f10 179 176 2012-09-13T19:32:14Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' fa6b2c12165602be9a3d3f3d21a480c5460e4f6d 180 179 2012-09-13T19:32:32Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' def489ae3d57eb9d1faf5a66fbb586c277361faa 181 180 2012-09-13T19:33:10Z Een 3 /* Game preferences */ wikitext text/x-wiki == The game == Select '''adjacent colored tokens''' in order to pop them. Strategically pop colored tokens in order to finish the game with the most of your own secret color popped. White tokens can be used as '''any color'''. Win '''instantly''' by popping all of your secret colored tokens. You must pop '''all''' adjacent tokens of the same color. In case of a tie at the end of the game, it's the player who popped '''the less''' of the tokens matching his own secret color who wins. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' ee464ecfaa154f5d4bfc530f7fa81ec68dcbea69 Gamehelpcantstop 0 35 170 93 2012-09-10T02:58:19Z Wmlwmsh 1387 wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' Roll 4 standard dice and then choose the sums of any 2 dice to advance your position on the board. You are allowed to use 3 temporary markers to track your progress in columns marked 2 through 12. These markers are commonly white or black. You can continue to roll the dice hoping to roll one of the previous combinations in the three columns selected this turn. When you think you've pushed your luck far enough you say "STOP" then pass the dice to the next player and replace the white markers with your own. If you roll the dice and find no useful combination you fail and your progress is lost. The more common numbers on 2d6 (6,7 and 8) have more steps in their paths while the less common combinations (2 and 12) have less. '''The object of the game is''' to reach and cover 3 peaks of these columns! 欲罷不能 如何進行遊戲 首先擲出四顆骰子,之後把四顆骰子兩兩分組配成可以搭配的任意數字(2~12),之後選擇其中一種配對執行。 執行時你可以擁有三個暫時的標記物可以作為前進的工具(此回合超過第三個就不能選該組合中的該項數值,但是其中一項可以前進即可選擇,另外一總組合則視同放棄) 重複以上動作直到你覺得這回合你前進已經夠了或是擲出你沒有辦法前進的骰子點數組合則該回合結束 若是自己喊出「夠了」則在現有的暫時標記物的位置換成自己的標記物輪下一人進行 若是因為擲出不能前進的骰子組合則是本回合前進的格數全部作廢依然換下一人進行回合 遊戲中使用的是兩個六面骰,依照組合的出現機率不同,在不同組合上要攻頂的需求步數也不同(例如7就需要最多格,因為他的出現率最高,相對的比較難出現的2.12就只需要短短的三格) 遊戲獲勝條件是其中有一人佔領了三條路線後則遊戲結束,由佔領三條路線的人獲勝 ea154f34371b9cb6ccc214ccbd3593b52dd35281 Tiedosto:ColorADD.jpg 6 45 173 2012-09-13T17:36:42Z Een 3 ColorADD Synthesis Panel wikitext text/x-wiki ColorADD Synthesis Panel b91230a1bcd980ad210bfef7eda6568197448d37 177 173 2012-09-13T17:53:14Z Een 3 uploaded a new version of &quot;[[File:ColorADD.jpg]]&quot; wikitext text/x-wiki ColorADD Synthesis Panel b91230a1bcd980ad210bfef7eda6568197448d37 178 177 2012-09-13T17:55:55Z Een 3 uploaded a new version of &quot;[[File:ColorADD.jpg]]&quot; wikitext text/x-wiki ColorADD Synthesis Panel b91230a1bcd980ad210bfef7eda6568197448d37 Rating 0 16 182 107 2012-09-14T13:37:14Z Anthonyw 1416 /* How many points do I gain / lose for each game ? */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you lose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or loose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you loose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or loose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == The formula is exactly one's used by the ELO rating system, with the following adjustments: * At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * K=60 for the first 30 games, K=40 afterwards. * K is multiply by (N/2) for N-players games. If N exceed the "advised number of players" for this game, K is multiply by (A/2), where A is the "advised number of players". * If 2 players has a rating difference greater than 600, we consider that their ELO difference is 600. == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO ac5f5c52715ee05bbf2d34ef4e9b941c675c66ba 183 182 2012-09-14T13:38:46Z Anthonyw 1416 /* How is my ELO ranking computed ? */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you lose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you loose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or lose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you lose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or lose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == The formula is exactly one's used by the ELO rating system, with the following adjustments: * At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * K=60 for the first 30 games, K=40 afterwards. * K is multiply by (N/2) for N-players games. If N exceed the "advised number of players" for this game, K is multiply by (A/2), where A is the "advised number of players". * If 2 players has a rating difference greater than 600, we consider that their ELO difference is 600. == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 5b02a5186bab27aa010cb24915cd423d1d4d795c 184 183 2012-09-14T13:39:14Z Anthonyw 1416 /* Some advice */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you lose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you lose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or lose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you lose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or lose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == The formula is exactly one's used by the ELO rating system, with the following adjustments: * At first, when someone left a game for any reason this game is not taken into account by the ELO rating system. * K=60 for the first 30 games, K=40 afterwards. * K is multiply by (N/2) for N-players games. If N exceed the "advised number of players" for this game, K is multiply by (A/2), where A is the "advised number of players". * If 2 players has a rating difference greater than 600, we consider that their ELO difference is 600. == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO 29eee9580bc23d4a4b15f58a2c641902fc4e17bc The Boss 0 46 186 2012-10-08T03:20:42Z Surgeonufo 992 Created page with "Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden u..." wikitext text/x-wiki Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each play: 1) May place gangsters on a city; the large gangsters are returned at the end of each round, while the small ones are not. After placing the gangsters, you must have more of them than any opponent. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 2 turns. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. bf53f1c840b684fa8d44a92ff430b74457a9ad71 187 186 2012-10-08T03:30:16Z Surgeonufo 992 wikitext text/x-wiki Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city; the large gangsters are returned at the end of each round, while the small ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 2 turns. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 72e05d23ee671ebfc3e694b7701c429bd6adb695 188 187 2012-10-08T03:58:44Z Surgeonufo 992 wikitext text/x-wiki Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city; the large gangsters are returned at the end of each round, while the small ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 2 turns. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 0984ee8026d2b253a4505a17cc83008844ab701f 189 188 2012-10-10T00:35:14Z Surgeonufo 992 wikitext text/x-wiki Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city; the large gangsters are returned at the end of each round, while the small ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 1cd0608a6589ed0b110ea3d8a710fecfac78d2cd 194 189 2012-10-21T16:41:16Z Zkiller 1253 wikitext text/x-wiki Description == Headline text == Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 7218e79b6f4ffa499d10c3c79258f17d0df6cea1 195 194 2012-10-21T16:41:46Z Zkiller 1253 wikitext text/x-wiki Description Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 2d47a12f90977587d13ecf3b85fc415bb6a0e154 196 195 2012-10-21T16:42:15Z Zkiller 1253 wikitext text/x-wiki Description Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, flip over the police card. This will be the final round if there are 3 police symbols of the same color. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. c999bf38999a5f8e8cb1a90ca507e252c3b2823c 197 196 2012-10-21T16:50:12Z Zkiller 1253 wikitext text/x-wiki Description Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. bb04a8cb3402651b892b179ee2ebca895c2383a3 198 197 2012-10-21T16:56:52Z Zkiller 1253 wikitext text/x-wiki == Goal == Be the player with the most money at the end of the game. At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. == Description == Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 432dfa10f670d4f18e73cc081f9ca0c85fa6c5b2 199 198 2012-10-21T16:59:09Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game. == Description == Each city, except Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. CHICAGO: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. c19d7496df06f0519437e32618a00b0d1772c5bc 200 199 2012-10-21T17:01:57Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game. == Description == Each city, except Chicago*, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. Chicago* city: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. a6efd2dc5d10193fb18c67e46893ebc0795fe90f Development 0 47 190 2012-10-10T19:53:06Z Een 3 Created page with "== TODO ==" wikitext text/x-wiki == TODO == e1ac7bc92296781c3f0c5713f7149c6d5fe6804f Help 0 4 191 77 2012-10-11T00:52:25Z Surgeonufo 992 /* Games */ wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailed help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] == Games == * [[Gamehelphaggis|Haggis]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpdominion|Dominion]] * [[Gamehelpyearofthedragon|In the Year of the Dragon]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpgosu|Gosu]] * [[Gamehelppuertorico|Puerto Rico]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelptobago|Tobago]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpunclechesnutstablegype|Uncle Chesnut's Table Gype]] * [[Gamehelptheboss]] 0174c39e1b6df9275ad2ba8f3ecaf2a6edaadb52 Gamehelpcoloretto 0 33 192 86 2012-10-12T06:06:20Z Texkill 1265 wikitext text/x-wiki While the regular game requires you to decide which piles to score positive/negative and where to place the jokers, this version automates that to give you the highest score possible. b9d4a44f0c124786a751dca5b76d3352df39041a Gamehelpraceforthegalaxy 0 41 193 143 2012-10-12T15:13:03Z Ksasaki 1566 wikitext text/x-wiki In Race for the Galaxy, players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods, and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, consuming goods, and bonuses from special developments. There are 5 phases to choose from: explore, develop, settle, consume, and produce. Players pick 1 phase per turn ( two phases in a 2 player game), then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player plays 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. abca15cea5052494619b6d5f1d5ba12ae3d0805c The Boss 0 46 201 200 2012-10-21T17:03:05Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except *Chicago, has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. *Chicago: Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 68de3fcea9657fd8d02ccb5635c83e6c62065b91 202 201 2012-10-21T17:03:43Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 7d6daad93cb2c130391c731332e7aacde688aad8 203 202 2012-10-21T17:03:59Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. BEGINNER TIPS The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 7c6b37bc7ca7145555d5c1f1ade750f39b1a0a7f 204 203 2012-10-21T17:05:02Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == The cards for each city are listed at the bottom, so if you have 3 Boston cards or 2 Philly cards you know a lot about what those cities are likely to be. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. 2a2298bf5bd374e53398e5cfb1215e89d24d339b 205 204 2012-10-21T17:07:02Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 8fd168878a6d30bb8a84c55609644fcef118bdcc 206 205 2012-10-21T17:08:29Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 071d649f531f765999f2a75340a28612fb75676f 207 206 2012-10-21T17:09:08Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. b36f7ba25cf6739be80f8c65b37507f849d68230 208 207 2012-10-21T17:09:22Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. f89411ca7e04c9d264393ef1174ff70d77d55cc7 209 208 2012-10-21T17:09:50Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. ac01719485336c4476e486c5b66a0948146bb834 210 209 2012-10-21T17:13:33Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 15927b932ec12efb6e7c759531bd55c4c47aa256 211 210 2012-10-21T17:15:26Z Zkiller 1253 /* Description */ wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. === End === When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. fbb0032ad54b25bf8e79797d9caf16f06c92d337 212 211 2012-10-21T17:16:04Z Zkiller 1253 /* Description */ wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. === End === When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 8a2f3be62a5d4dfa60387a18b4294141548a054e 213 212 2012-10-21T17:16:57Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. === End === When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. ad560d07ee3da3a29fd361d5f71106e7d7da192c 214 213 2012-10-21T17:17:18Z Zkiller 1253 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. === End === When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. be896aa3e963c564804a2ad2687dd5f142db909b 215 214 2012-10-21T17:18:18Z Zkiller 1253 /* Description */ wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 0ac56904907b213c1616a11bbaa9c40d0d45a0a2 216 215 2012-10-21T17:18:52Z Zkiller 1253 /* Description */ wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 216df9ddb16c37d4791196cf9503d42bd9c7b533 217 216 2012-10-21T18:32:21Z Zkiller 1253 moved [[Gamehelptheboss]] to [[The Boss]]: changing the temporary name wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 216df9ddb16c37d4791196cf9503d42bd9c7b533 Gamehelptheboss 0 48 218 2012-10-21T18:32:21Z Zkiller 1253 moved [[Gamehelptheboss]] to [[The Boss]]: changing the temporary name wikitext text/x-wiki #REDIRECT [[The Boss]] 525eb3913da3d347e1fc98bce9e9581c9311dde1 Help 0 4 219 191 2012-10-21T18:34:06Z Zkiller 1253 /* Games */ wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailed help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] == Games == * [[Gamehelphaggis|Haggis]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpdominion|Dominion]] * [[Gamehelpyearofthedragon|In the Year of the Dragon]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpgosu|Gosu]] * [[Gamehelppuertorico|Puerto Rico]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelptobago|Tobago]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpunclechesnutstablegype|Uncle Chesnut's Table Gype]] * [[Gamehelptheboss|The Boss]] c32d04b6feec5c8e94d0443e6936e954d3e3d428 Studio 0 49 220 2012-10-24T20:17:33Z Sourisdudesert 1 Created page with "'''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) S..." wikitext text/x-wiki '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 8bebe049b7e073dc301e36218d40bd9907ed708a 222 220 2012-10-24T20:24:57Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 2b9e8a86566104378c88ee21ca98a6dac0bf5b5c 223 222 2012-10-24T20:33:05Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == At first, check out these presentations: == BGA Studio documentation == f9da3165f2dbc6f8331cbf2a9e4e2c321388d1d4 224 223 2012-10-24T20:40:16Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] == BGA Studio documentation == e12da43129600f5b6278753f91665ff8dbfccc27 225 224 2012-10-24T20:42:01Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * The 8 steps to create a board game on Board Game Arena == BGA Studio documentation == 5a9d214a8612c2563cbaad4cb5d70f3b7b7141b4 226 225 2012-10-24T20:52:50Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] == BGA Studio documentation == (available soon...) 1bea11654a7ab6eb5b382657f96413c6797ba21e 228 226 2012-11-22T10:28:01Z Sourisdudesert 1 /* How to develop a board game on BGA Studio? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == (available soon...) a51c138b49e90fc4c1382cfe9ce0daf64b17678c Tiedosto:Bga studio small.jpg 6 50 221 2012-10-24T20:24:25Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Gamehelpintheyearofthedragon 0 51 227 2012-11-18T22:32:20Z Salty-horse 1046 Add end-of-round and -game scoring reminders wikitext text/x-wiki === End-of-round Scoring === * 1 point for each palace. * 1 point For each court lady. * 1 point For each privilege. (Small privilege = 1 point; Large privilege = 2 points) === End-of-game Scoring === * 2 points for each person tile. * For each monk: score [number of Buddhas] x [number of floors] points. * 1 point for each 3 yuan. 094e726d8bed26a5b54c43e6aa40764bda2b5679 Gamehelphearts 0 52 229 2012-11-22T12:36:19Z Daleb147 1803 Created page with "Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving ..." wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your cards, choose to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player on your right, (3rd hand) to the player across the table, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as shooting the moon), that player scores 0 and all other players score -26. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. c028cf9094219edf61eff451dedac653ab7fd8f2 230 229 2012-11-22T12:38:14Z Daleb147 1803 wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your cards, choose to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player on your right, (3rd hand) to the player across the table, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as shooting the moon), that player scores 0 and all other players score -26. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. c8041d0b7782fad9106ef871a23dc9af7593637e 231 230 2012-11-22T12:39:33Z Daleb147 1803 wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your hand, choose three cards to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player on your right, (3rd hand) to the player across the table, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as shooting the moon), that player scores 0 and all other players score -26. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. 24db4367246faacd715235883032541395f40c23 234 231 2012-11-22T23:25:47Z Daleb147 1803 wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your hand, choose three cards to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player across the table, (3rd hand) to the player on your right, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as shooting the moon), that player scores 0 and all other players score -26. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. c134a170f6a54f64352aab39c6d476725b8c269f Development 0 47 232 190 2012-11-22T19:40:45Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == (available soon...) a51c138b49e90fc4c1382cfe9ce0daf64b17678c 233 232 2012-11-22T19:43:23Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === === What do I get? === === How should I start? === === Other ressources === FAQ Files reference Framework functions reference Development forum a26867be4e544f9a5579183b085ec9ad12fe79fe 235 233 2012-11-23T09:05:14Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is opened to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How to develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us|BGA contact address]. Please provide the following information: * developer's user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * developer's real name; * developer's email address; * developer's postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions document for developers on BGA Studio joined as an attachment. We also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Then, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login and complex password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initiated from our 'emptygame' template, providing the game structure (and comments! and examples!) Then you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === FAQ Files reference Framework functions reference Development forum bf764d6703bea2d63e7e6ca0e028982e48cd8292 236 235 2012-11-23T09:10:43Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * developer's user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * developer's real name; * developer's email address; * developer's postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions document for developers on BGA Studio joined as an attachment. We also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Then, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login and complex password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initiated from our 'emptygame' template, providing the game structure (and comments! and examples!) Then you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === [Development FAQ] [Development files reference] [Development framework functions reference] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 979e2f97593be050323fa3e481b8ba78467042da 237 236 2012-11-23T09:14:05Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your contact e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Then, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login and complex password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initiated from our 'emptygame' template, providing the game structure (and comments! and examples!) Then you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === [Development FAQ] [Development files reference] [Development framework functions reference] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 72bf370daa09b9f048a4b31e1b98a5506fa633e6 238 237 2012-11-23T09:15:00Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Then, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login and complex password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initiated from our 'emptygame' template, providing the game structure (and comments! and examples!) Then you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === [Development FAQ] [Development files reference] [Development framework functions reference] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] cf121fd0e36ab9d557973fb88647af87ee195063 239 238 2012-11-23T09:18:34Z Een 3 /* Ok, I registered, what do I get? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === [Development FAQ] [Development files reference] [Development framework functions reference] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] b6fda90a3132709481751181d2ce8aaff8f4862c 240 239 2012-11-23T09:21:31Z Een 3 /* Other ressources */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other ressources === [[Development FAQ]] [[Development files reference]] [[Development functions reference]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2e858b45e252006334dcd72c76fca2988fe9cf4a 243 240 2012-11-23T09:52:04Z Een 3 /* Other ressources */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] e47246dabfa0a690d90e5b0629fd2cf3916767d5 248 243 2012-11-24T09:56:14Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 0c08a29e8f25782d85af7351110d1819061d46b3 249 248 2012-11-24T11:08:45Z Een 3 /* Other resources */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'ressources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2653ee87c6344ebf07629f0b3f8249b5f53ad234 250 249 2012-11-24T11:12:59Z Een 3 /* Ok, I registered, what do I get? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: (TODO: link tutorials). Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 6a298e378936e884914ef321723c403658e558f1 Studio FAQ 0 53 241 2012-11-23T09:39:08Z Een 3 Created page with "This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Us..." wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. 245dd42acf9f030324bddb3016b307338c8f8fb1 242 241 2012-11-23T09:48:15Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. b6678b288dddeacf8990a5fb7fb0afa71067c2fd Studio file reference 0 54 244 2012-11-23T10:53:17Z Een 3 Created page with " This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This direct..." wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game: * game_box.png is displayed on the main site on the game description page and when creating a table (280x280 px) * game_icon.png is the icon displayed in the lists of games and tables (50x50 px) * publisher.png is the logo of the publisher of the game, displayed on the game description page (width: 150 px) * and other images that you need. You should use [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites] for better web performance. === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the states your game will iterate over, and the transitions to get from one state to another. === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. bb690d4d6d5bf813de7ac7ae7d19ade7323cecab Studio function reference 0 55 245 2012-11-23T11:00:23Z Een 3 Created page with "This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == ; todo ..." wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == ; todo : todo == Client side (Javascript functions) == ; todo : todo 533cc4280a0aee261ba9cca18e0a826f0de47487 Gamehelpdominion 0 29 246 76 2012-11-24T04:02:03Z Gelvo 1816 wikitext text/x-wiki '''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being depleted. '''Setup''' Each player starts the game with a deck of 10 cards (3 ''Estates'', and 7 ''Copper''). The deck is shuffled and 5 cards are drawn to form the player's hand. '''Overview''' The A-B-C rule makes it easy to remember the play order. On a player's turn: A: Action: The player may play an action card. Each player is allowed 1 action per turn by default. Certain action cards provide additional actions. B: Buy: After the action phase is over, the player may play buy a card from the general supply. Bought cards are placed in the player's discard pile. Each player is allowed 1 buy per turn by default. Certain action cards provide additional buys. C: Cleanup: After the buy phase is over, the player places all cards (played cards and in hand cards) into the discard pile. Five new cards are drawn from the player's deck. If there aren't enough cards to form a full hand, the discard pile is reshuffled and forms the new deck. The game progresses in clockwise order until ending conditions are met. '''Tie-breaker''' If 2 or more players have the same number of victory points, the player that had the fewest number of turns wins. If there is still a tie, all tied players rejoice in a shared victory. '''Learning About Each Card''' To learn about each card in Dominion you can go to [http://deckengine.info/dominion/] 199dbbce7742bec95e581d7c67e7d3e6aafed394 247 246 2012-11-24T04:03:28Z Gelvo 1816 wikitext text/x-wiki '''Goal''' Be the player with the most victory points at the end of the game. '''Ending Condition''' The ''Province'' stack being depleted, or 3 stacks of cards being depleted. '''Setup''' Each player starts the game with a deck of 10 cards (3 ''Estates'', and 7 ''Copper''). The deck is shuffled and 5 cards are drawn to form the player's hand. '''Overview''' The A-B-C rule makes it easy to remember the play order. On a player's turn: A: Action: The player may play an action card. Each player is allowed 1 action per turn by default. Certain action cards provide additional actions. B: Buy: After the action phase is over, the player may play buy a card from the general supply. Bought cards are placed in the player's discard pile. Each player is allowed 1 buy per turn by default. Certain action cards provide additional buys. C: Cleanup: After the buy phase is over, the player places all cards (played cards and in hand cards) into the discard pile. Five new cards are drawn from the player's deck. If there aren't enough cards to form a full hand, the discard pile is reshuffled and forms the new deck. The game progresses in clockwise order until ending conditions are met. '''Tie-breaker''' If 2 or more players have the same number of victory points, the player that had the fewest number of turns wins. If there is still a tie, all tied players rejoice in a shared victory. '''Learning About Each Card''' To learn about each card in Dominion you can go to Deck Engine[http://deckengine.info/dominion/] d8bee17613455731e1204adcab26ce10795c6d0b Studio back-office 0 56 251 2012-11-24T11:38:02Z Een 3 Created page with "You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in thi..." wikitext text/x-wiki You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. === Sources === Contains a form for committing your sources to the BGA repository. Game name field should contain the name of your game (lower case, no space). Comment should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking us) to get a previous version of your code if you need to backtrack. === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error logs happening at the table you are currently playing at ; Current table request logs : this log traces all the actions happening at the table you are currently playing at ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. a75366b91b41d11f5f611dee233b0deee2fc46c7 252 251 2012-11-24T11:41:09Z Een 3 wikitext text/x-wiki You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. === Sources === Contains a form for committing your sources to the BGA repository. * Game name field should contain the name of your game (lower case, no space). * Comment field should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error happening at the table you are currently playing at. ; Current table request logs : this log traces all the actions happening at the table you are currently playing at. ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. 5623b1560a6ae757eaa2f53c2494e6edda3eab19 Tutorial reversi 0 57 253 2012-11-24T13:03:49Z Sourisdudesert 1 Created page with "blabla" wikitext text/x-wiki blabla bb21158c733229347bd4e681891e213d94c685be 254 253 2012-11-24T13:09:13Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. c26a5af8e6828564ab4ee223d229dc54a9b9286d 255 254 2012-11-24T13:14:14Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == 1af09db09aef0c5098f0fa2a17b2ea9068a1de79 256 255 2012-11-24T13:15:59Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. So, let's upload our board. f60e6e9884c37a2e73df5b02b3666c9caa1f5154 258 256 2012-11-24T13:27:24Z Sourisdudesert 1 /* Let it look like Reversi */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a <div> for your board <nowiki> <div id="board"> </div> </nowiki> 664e12899137864d7e95e67481c51c9a053b8da9 259 258 2012-11-24T13:33:03Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <nowiki> <div id="board"> </div> </nowiki> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } b25c53195c653db74860f7375650536de492b023 261 259 2012-11-24T13:34:55Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <nowiki> <div id="board"> </div> </nowiki> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] 8bdac21920f65576aeff896481ae1b41e77998bf 262 261 2012-11-24T13:41:58Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> 2aa9b4055aa2b3b3bd8d72e989c3c0d2d8bf7541 263 262 2012-11-24T13:49:19Z Sourisdudesert 1 /* Make the squares appears */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. e8f724d68a8ff40cf5c1e30ee29a194a91df7e95 264 263 2012-11-24T13:50:24Z Sourisdudesert 1 /* Make the squares appears */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] 9f506bb9b9388809e70f528325323a3ea3820bdc 267 264 2012-11-24T13:57:41Z Sourisdudesert 1 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Then, let's design some white&black disc tokens with a little bit of CSS d6c98d457f8c2baf69ddcb3ea2de8566b739ac7d 269 267 2012-11-24T14:00:05Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Then, let's design some white&black disc tokens with a little bit of CSS 45d86a617f0bcaabd8a2a2e713ff1ddf0d7b6828 270 269 2012-11-24T14:05:02Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "javascript template". aea59cd9a0d5a655a1bfc40de5928cb39c07cd42 271 270 2012-11-24T14:15:06Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. 61619f1af84c80d04243ea001a6082b77b3e6dac 272 271 2012-11-24T14:18:04Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] f476c6f09986a1589113b80bcde9b224e8b37e07 274 272 2012-11-24T14:22:59Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Note: of course we can't explain how d9c8d7a3f87db8034e553a1b158b1bdf75e7e4ea 275 274 2012-11-24T14:23:39Z Sourisdudesert 1 /* Introduction */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Note: of course we can't explain how 109fc90a2a4ccc1e28bfec0b718896f72258b878 276 275 2012-11-24T14:30:01Z Sourisdudesert 1 /* The database */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. == Setup the initial game position == Initially, there are 4 tokens on the board (2 white, 2 black). f3bd28bcc75748f33ef11ca30faa653345b6eba5 277 276 2012-11-24T14:38:27Z Sourisdudesert 1 /* Setup the initial game position */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. 5bd54f607111d419b76ae50aad1bee09b1fce065 278 277 2012-11-24T14:40:11Z Sourisdudesert 1 /* The database */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. 3740876f658f944737528e641e6352ddff6c27f6 280 278 2012-11-24T14:41:02Z Sourisdudesert 1 /* Setup the initial game position */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] 3baeffb6a7a725930a2c768e195a2650f5ae7750 282 280 2012-11-24T14:42:23Z Sourisdudesert 1 /* Setup the initial game position */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... b54d251dbf8e412c8a606c3625258999a7a14611 283 282 2012-11-24T14:43:09Z Sourisdudesert 1 /* The database */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... be60f242a3d7610172e71f72d90cdbd0119508ec 284 283 2012-11-24T14:47:37Z Sourisdudesert 1 /* Setup the initial game position */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] 26605e3dcebc2d859057134f2e4a955b9f084d7f 286 284 2012-11-24T14:49:10Z Sourisdudesert 1 /* The game state machine */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> 7f239e145406ed06bd3d5ea48e7abc90f39f58ee 287 286 2012-11-24T14:52:34Z Sourisdudesert 1 /* The game state machine */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture above, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] 45143c8f884d71113786b746e717385e427b1680 289 287 2012-11-24T15:07:07Z Sourisdudesert 1 /* The game state machine */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture above, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. 1d617cd8cd2e69715ffa9220f5d7e7c3070aadbc 290 289 2012-11-24T15:10:37Z Sourisdudesert 1 /* The rules */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture above, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed move == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above: 3c3e0f3dbdd3a75eea3c82143585d5912b6d1a4e 291 290 2012-11-24T15:10:55Z Sourisdudesert 1 /* The game state machine */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed move == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above: ebc5c345f749b004365eda4eef51ebbafd90d7c9 292 291 2012-11-24T15:18:41Z Sourisdudesert 1 /* Display allowed move */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] 1e8642b57da1bb69f6b242f3f05230046dbc9404 294 292 2012-11-24T15:30:07Z Sourisdudesert 1 /* Display allowed moves */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. 84b70487a17c62e442649e14de5f32556560912f 297 294 2012-11-25T10:24:14Z Sourisdudesert 1 /* Let's play */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> </pre> </pre> fd595cb9caea45c80ef81cdcf140310731e6d701 298 297 2012-11-25T10:30:46Z Sourisdudesert 1 /* Let's play */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). Now 64278699265cc34b9bc9f92c7be45317d9301059 299 298 2012-11-25T10:34:51Z Sourisdudesert 1 /* Let's play */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appear in the database: [[File:reversi9.jpg]] 6101cdabac21b0201d459cc1468064208a53397e Tiedosto:Board.jpg 6 58 257 2012-11-24T13:16:48Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi1.jpg 6 59 260 2012-11-24T13:34:17Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi2.jpg 6 60 265 2012-11-24T13:50:36Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 266 265 2012-11-24T13:50:59Z Sourisdudesert 1 uploaded a new version of &quot;[[File:Reversi2.jpg]]&quot; wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Tokens.png 6 61 268 2012-11-24T13:58:04Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi3.jpg 6 62 273 2012-11-24T14:18:16Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi4.jpg 6 63 279 2012-11-24T14:40:25Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi5.jpg 6 64 281 2012-11-24T14:41:52Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi6.jpg 6 65 285 2012-11-24T14:47:49Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi7.jpg 6 66 288 2012-11-24T14:53:29Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Reversi8.jpg.jpg 6 67 293 2012-11-24T15:18:51Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Development 0 47 295 250 2012-11-24T17:46:44Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick 'terms & conditions' document. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document (TODO: link pdf) as an attachment, and the sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] eecf066a299c4dc0c3a8e68add20e2673f238869 Studio FAQ 0 53 296 242 2012-11-24T18:17:21Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "go to database" link at the bottom of your game. This link will bring you directly to the database for the current table. c077ff8abbf540a10b8cb8c5557634d59d5d85ff Tiedosto:Reversi9.jpg 6 68 300 2012-11-25T10:35:06Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tutorial reversi 0 57 301 299 2012-11-25T10:37:05Z Sourisdudesert 1 /* Let's play */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == 4cd2519b3e4199201ca4dcc3b691f5ca772579af 322 301 2012-11-28T16:39:13Z Sourisdudesert 1 /* Make the move appears automatically */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> 03d8de1fa331667d24bce827ab6b49abd375f329 323 322 2012-11-28T16:43:41Z Sourisdudesert 1 /* Make the move appears automatically */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on BGA environment: Reversi game. Before reading this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It is always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful during designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, then faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements when squares are. These elements will be used as position references for width&black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, is we apply the classes "token" and "tokencolor_ffffff" to a div element, we got a white token. Yeah. Note the "position: absolute" which allow us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appears on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". d69108e07a1157d677d0be32ad3ad5f0bf653860 Studio FAQ 0 53 302 296 2012-11-25T12:38:59Z Een 3 /* Is there a quick way to access the database for my current table? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. d08951d5aeff9840f161e2f42c60814d94b4a982 314 302 2012-11-27T20:52:20Z Een 3 moved [[Development FAQ]] to [[Studio FAQ]] wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. d08951d5aeff9840f161e2f42c60814d94b4a982 329 314 2012-11-30T13:13:17Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ) In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. e6503c44ae66f7402062c4967deadea42a478c0e 330 329 2012-11-30T13:13:43Z Een 3 /* Is there a special way to declare the strings that must be translated? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == Please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. 347ddc92e9e7cbfe28522592fc2550230373ab27 345 330 2012-12-05T19:50:05Z Een 3 /* I updated the images in the 'img' folder of my game, but they don't show? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that serves to launch the games. The game icon, box and the publisher logo are hosted on the mainsite server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the Studio back office to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. 4b25daf1b81ba2f65df3a679e295cb84e26d2a09 346 345 2012-12-05T19:59:56Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that serves to launch the games. The game icon, box and the publisher logo are hosted on the mainsite server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. 639042654b8518f33cb968566c4b2a10df9ada31 347 346 2012-12-05T20:01:10Z Een 3 /* I updated the images in the 'img' folder of my game, but they don't show? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. c08c1dd70f4dc43128232e20b3f7bb9a1a4e9644 Development 0 47 303 295 2012-11-25T16:46:42Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [['terms & conditions' document:BGA_TC_Dev_en.pdf]]. It's really quick, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment and the following sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] ea9b6c9329378a3c6087764ef21789fa4045658c 310 303 2012-11-25T21:20:30Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment and the following sentence 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 250ad0fb0beb3717c7ba8e705c8e07a5af724385 311 310 2012-11-25T21:22:25Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) === Other resources === [[Development FAQ]] [[Development file reference]] [[Development function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 5af77415355ccf88e63dafcfd73ca902364ca78d 316 311 2012-11-27T20:53:07Z Een 3 /* Other resources */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == BGA Studio documentation == === How do I register? === Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! === Ok, I registered, what do I get? === First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! === Great, I'm in! ... How should I start? === If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) === Other resources === [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] c025722f9ad5f6199ddad7cb28099f0eeee36959 321 316 2012-11-27T20:54:33Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == How do I register? == Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek to tutorials for the example games: * [[Tutorial reversi]] * [[Tutorial hearts]] * [[Tutorial xxx ]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] cd840ccedee7bdbbb48175292cdbc187adffda9e 328 321 2012-11-29T23:08:13Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == How do I register? == Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at tutorials for two example games: * [[Tutorial reversi]] * [[Tutorial gomoku ]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2d04ceb441b20e7d1be485478ad8716df8f72b6a 332 328 2012-11-30T13:50:28Z Een 3 /* How can I develop a board game on BGA Studio? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at tutorials for two example games: * [[Tutorial reversi]] * [[Tutorial gomoku ]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 39257b26b818eda07805de2338e456c3b42df777 333 332 2012-11-30T13:54:00Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 5f43c2d8a01bca25cac35caa417c67c5428ecf07 334 333 2012-11-30T13:54:35Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail to [http://en.boardgamearena.com/#!doc/Contact_us BGA contact address]. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 72dc57200d6a3312dadb9ff5d2d1aaa7c3d73e0e 336 334 2012-11-30T14:35:09Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail to studio(at)boardgamearena.com. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 7bd6cd9d30a6adb2837daf46206634e2e087c3cb 337 336 2012-11-30T14:35:27Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at studio(at)boardgamearena.com. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 4bd37a3234a5ff6846a7e6a0a266ea4d532a6874 338 337 2012-11-30T14:35:54Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, you get by e-mail a list of the games we have an agreement to develop. Please keep this list confidential, or it would ruin our tradition to make players guess the name of the next game on the forum! <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 0460b4756dc697bb7e2c0313c08d9d41e4365a2b 339 338 2012-11-30T14:57:01Z Een 3 /* Ok, I registered, what do I get? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum!) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] ec37ab7db579bad08c6bd4b2d8944c42f8289445 340 339 2012-11-30T14:57:41Z Een 3 /* Ok, I registered, what do I get? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 9ed07dc33eb79a958736a93bf3dc10151ecdd400 341 340 2012-11-30T15:03:35Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]! == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] d2385456525acba6606d1944f34d9bac47a0460a 342 341 2012-11-30T15:03:57Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 01f7f2a589a553ea6267d12e68f7b0cd5907a607 344 342 2012-11-30T15:50:47Z Een 3 Redirected page to [[Studio]] wikitext text/x-wiki #REDIRECT[[Studio]] 75f930ede7d4ad1a62943418c718a02b755fc4d5 Studio function reference 0 55 304 245 2012-11-25T18:37:20Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class <gamename>.action.php === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === == Client side (Javascript functions) == ; todo : todo 5a8d76a6197493599d8d3a89cb9447ed0edc7c2b 305 304 2012-11-25T18:57:56Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row == Client side (Javascript functions) == ; todo : todo c8e9bb737a440acb0eee50a8b34aa14ef8a13edb 306 305 2012-11-25T19:04:06Z Een 3 /* Table class (.game.php) */ wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row == Client side (Javascript functions) == ; todo : todo 0b01bb492d9f22816dcd327e0def11a3e8abe281 307 306 2012-11-25T19:15:02Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; todo : todo 64a5654f4a93ed215c0270c01cde118163a7664a 308 307 2012-11-25T21:10:34Z Een 3 /* Client side (Javascript functions) */ wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.slideToObjectPos( $('token_'+token.token_id), $('cp_background'), x_pix, y_pix, 10 ).play(); ; this.isSpectator ; this.player_id ; this.updateCounters(gamedatas.counters) ; this.addTooltipToClass( 'cp_token_icon_color1', _('Green'), '' ); ; this.addEventToClass( "cp_token", "onclick", "onClickToken"); ; this.isCurrentPlayerActive() ; this.checkAction( 'selectTokens' ) ; this.showMessage( _('You must select at least two tokens'), 'error' ); ; this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); ; this.updateCounters(notif.args.counters); ; this.ajaxcall( "/colorpop/colorpop/endgame.html", { }, this, function( result ) {} ); b8930b0747d4b187b304b90ae7bc63c4996b456f 317 308 2012-11-27T20:53:20Z Een 3 moved [[Development function reference]] to [[Studio function reference]] wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.slideToObjectPos( $('token_'+token.token_id), $('cp_background'), x_pix, y_pix, 10 ).play(); ; this.isSpectator ; this.player_id ; this.updateCounters(gamedatas.counters) ; this.addTooltipToClass( 'cp_token_icon_color1', _('Green'), '' ); ; this.addEventToClass( "cp_token", "onclick", "onClickToken"); ; this.isCurrentPlayerActive() ; this.checkAction( 'selectTokens' ) ; this.showMessage( _('You must select at least two tokens'), 'error' ); ; this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); ; this.updateCounters(notif.args.counters); ; this.ajaxcall( "/colorpop/colorpop/endgame.html", { }, this, function( result ) {} ); b8930b0747d4b187b304b90ae7bc63c4996b456f 324 317 2012-11-29T22:47:37Z Een 3 /* Client side (Javascript functions) */ wikitext text/x-wiki This page references useful server side and client side functions, so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on which browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). ; 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; this.addEventToClass( "cp_token", "onclick", "onClickToken"); ; this.isCurrentPlayerActive() ; this.checkAction( 'selectTokens' ) ; this.showMessage( _('You must select at least two tokens'), 'error' ); ; this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); ; this.updateCounters(notif.args.counters); ; this.ajaxcall( "/colorpop/colorpop/endgame.html", { }, this, function( result ) {} ); 2883f9c0abb0ad4818ba23199536621e31463a82 325 324 2012-11-29T23:04:27Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). ; 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 196b47f3b6d139d77a359829cbfa749061bc3d73 326 325 2012-11-29T23:06:57Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). ; 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 3cbe8ea5ebf585d8ace0752c8030f718dd2fe28b 327 326 2012-11-29T23:07:06Z Een 3 wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). ; 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 55c96307b77bd59db8d1da2247aa971adf57c54a Tiedosto:BGA TC Dev en.pdf 6 69 309 2012-11-25T21:17:46Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 331 309 2012-11-30T13:33:17Z Een 3 uploaded a new version of &quot;[[File:BGA TC Dev en.pdf]]&quot; wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Studio back-office 0 56 312 252 2012-11-27T20:50:47Z Een 3 wikitext text/x-wiki You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. === Sources === Contains a form for committing your sources to the BGA repository. * Game name field should contain the name of your game (lower case, no space). * Comment field should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error happening at the table you are currently playing at. ; Current table request logs : this log traces all the actions happening at the table you are currently playing at. ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. b8dac291fbe5b8e2412fc015dff9c7f432d8bd59 313 312 2012-11-27T20:51:22Z Een 3 wikitext text/x-wiki You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. === Sources === Contains a form for committing your sources to the BGA repository. * Game name field should contain the name of your game (lower case, no space). * Comment field should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error happening at the table you are currently playing at. ; Current table request logs : this log traces all the actions happening at the table you are currently playing at. ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. cdb10c7884bbf321c65a6abd52427eee6870b1b3 Development FAQ 0 70 315 2012-11-27T20:52:20Z Een 3 moved [[Development FAQ]] to [[Studio FAQ]] wikitext text/x-wiki #REDIRECT [[Studio FAQ]] 0e0d928fd9ebb07aa5cabcbcb800036357516c70 Development function reference 0 71 318 2012-11-27T20:53:20Z Een 3 moved [[Development function reference]] to [[Studio function reference]] wikitext text/x-wiki #REDIRECT [[Studio function reference]] 0d4e31ae78128f335e8e652b8bd209497bea7540 Studio file reference 0 54 319 244 2012-11-27T20:53:33Z Een 3 moved [[Development file reference]] to [[Studio file reference]] wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game: * game_box.png is displayed on the main site on the game description page and when creating a table (280x280 px) * game_icon.png is the icon displayed in the lists of games and tables (50x50 px) * publisher.png is the logo of the publisher of the game, displayed on the game description page (width: 150 px) * and other images that you need. You should use [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites] for better web performance. === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the states your game will iterate over, and the transitions to get from one state to another. === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. bb690d4d6d5bf813de7ac7ae7d19ade7323cecab Development file reference 0 72 320 2012-11-27T20:53:33Z Een 3 moved [[Development file reference]] to [[Studio file reference]] wikitext text/x-wiki #REDIRECT [[Studio file reference]] 7e0b4170c41b68fcf9b09724d3d1b7bf8de5a76e Tutorial gomoku 0 73 335 2012-11-30T13:56:04Z Een 3 Created page with "TODO" wikitext text/x-wiki TODO b698c11e84460ed5999bfeb5cbf25b865c238f3c Studio 0 49 343 228 2012-11-30T15:49:04Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at this tutorial for creating the Reversi example game: * [[Tutorial reversi]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 01f7f2a589a553ea6267d12e68f7b0cd5907a607 Tiedosto:Gomoku tuto1.png 6 74 348 2012-12-05T21:06:58Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Gomoku tuto2.png 6 75 349 2012-12-05T21:07:10Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Gomoku tuto3.png 6 76 350 2012-12-05T21:07:19Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Gomoku tuto4.png 6 77 351 2012-12-05T21:07:29Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Gomoku tuto5.png 6 78 352 2012-12-05T21:07:38Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Gomoku tuto6.png 6 79 353 2012-12-05T21:07:49Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tutorial gomoku 0 73 354 335 2012-12-05T21:09:23Z Een 3 wikitext text/x-wiki Here is how your games look by default when it has just been created : [[File:Gomoku tuto1.png]] Gather useful images for the game and edit them as needed. Edit .tpl & .css to add the board : [[File:Gomoku tuto2.png]] Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action. Add game action in .game.php to update the database, then notify the client using a method ‘’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones. [[File:Gomoku tuto4.png]] Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] Implement specific rules for the game (if any) Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js Test everything thoroughly... you are done! [[File:Gomoku tuto6.png]] fcf0d63e99019e009a64cc2f45a3061e0997cdd4 355 354 2012-12-05T21:14:00Z Een 3 wikitext text/x-wiki == Start from the emtpy game template == Here is how your games look by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Edit .tpl & .css to add the board : [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action. Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js Test everything thoroughly... you are done! [[File:Gomoku tuto6.png]] 770a1936ddb073d24fc46111d83b4de2f07be7e9 356 355 2012-12-05T21:14:15Z Een 3 /* Start from the emtpy game template */ wikitext text/x-wiki == Start from the emtpy game template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Edit .tpl & .css to add the board : [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action. Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js Test everything thoroughly... you are done! [[File:Gomoku tuto6.png]] e4b849c06439e1657646ff876b0611ecc4b37c58 357 356 2012-12-05T21:17:54Z Een 3 wikitext text/x-wiki == Start from the emtpy game template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 5459b575222bb6886bbc92e33b2526c342c0f163 358 357 2012-12-05T21:18:27Z Een 3 /* Setup the board */ wikitext text/x-wiki == Start from the emtpy game template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 333f80b42a2a4503200c1d2594d04917358be984 359 358 2012-12-05T21:24:48Z Een 3 wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == Start from the emtpy game template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 9eb647a35af1b562d14557cf16d235693d35e22b 360 359 2012-12-05T21:25:33Z Een 3 /* Start from the emtpy game template */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 5a68b325ce531060787304be7f314dfe7510e33e 361 360 2012-12-05T21:26:10Z Een 3 /* Cleanup */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game condition(s) == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 994f40b68cfbaccf00b4bea0f7b2483b6370095e 362 361 2012-12-05T21:26:20Z Een 3 /* Implement rules and end of game condition(s) */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some div for the board in the HTML. Edit .css to show the image of the board as background. [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 812c4fea187e9a853f3dcbd0588770c7c11c1065 364 362 2012-12-05T21:58:44Z Een 3 /* Setup the board */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. Edit .game.php->setupNewGame to insert the empty intersections (19x19) with coordinates into the database. Edit .game.php->getAllDatas to retrieve the state of the intersections from the database. Edit .tpl to create a template for intersections (jstpl_intersection). Edit .js to setup the intersections layer that will be used to get click events and to display the stones. Use some temporary css colors on borders or background+opacity to make sure they are positioned right. You can declare some constants in material.inc.php and pass them to your .js through .game.php->getAllDatas for easy repositioning. [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] c0758d7154977a6725932786631d490d8a63eaba 365 364 2012-12-05T22:13:33Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit .sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> And pass them to your gomoku.js through gomoku.game.php->getAllDatas() for easy repositioning (modify constant, refresh). // Constants $result['constants'] = $this->gameConstants; Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 26ac9382325c962f7620ffdf4174e385a7ee1023 366 365 2012-12-05T22:13:56Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> And pass them to your gomoku.js through gomoku.game.php->getAllDatas() for easy repositioning (modify constant, refresh). // Constants $result['constants'] = $this->gameConstants; Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 842acc557f3052d95e38808148093d8e057a3a76 367 366 2012-12-05T22:18:34Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> And pass them to your gomoku.js for easy repositioning (modify constant, refresh). * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] a4b0f28f38a333e965030c81d36ed6b23dea298f 368 367 2012-12-05T22:20:55Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constant values have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 89de825d4f23bd359ed656f28be811507ee532a8 369 368 2012-12-05T22:21:30Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php Add onclick events on intersections in .js, calling an action with appropriate parameters Add action in .action.php, retrieving parameters and calling the appropriate game action Add game action in .game.php to update the database, then notify the client using a method ‘stonePlayed’ Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. The basic game turn is implemented: you can drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 05743b8feac3d4829fc79918f260e21ad40b936d 370 369 2012-12-05T22:36:37Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } Implement this method in javascript to update the intersection to show the stone, and register it inside the setNotifications function. notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, For this function, you need to declare a stone javascript template in your .tpl file var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 2b3fc4e1c8667563a2f5381b7c87a523442c89d4 371 370 2012-12-05T22:40:51Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; * to define the css styles for the stones .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } These styles rely on an image of both the white and black stones, and position the background appropriately to show only the part of the background image matching the appropriate stone. The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] b4fe94be1cfe6d346bb1587062e3544b3bd7e297 373 371 2012-12-05T22:45:13Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an image of both the white and black stones, and position the background appropriately to show only the part of the background image matching the appropriate stone or the white space if there is no stone. Here is what the image looks like (we will use the red circle later on): [[File:Gomoku stones.png]] The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 704504582a9dcb58e10995b39556b6e30a037514 374 373 2012-12-05T22:48:00Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like (we will use the red circle later on): [[File:Gomoku stones.png]] The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] d5c4971530420428f040193ef825f9854005ff6e 375 374 2012-12-05T22:55:18Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game (if any) Nothing special for Gomoku Implement rule for computing game progression in .game.php->getGameProgression() Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 779b1978f9b35636690f0a59f27ce681c331b559 377 375 2012-12-05T23:06:25Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner in .game.php->stCheckEndOfGame() Notify the score and implement the corresponding interface update in .js '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 43b1670c24100dd1aa9409735a8497555c99d047 393 377 2012-12-06T22:07:37Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner in gomoku.game.php->stCheckEndOfGame() * It is easier to check for a win directly after setting the stone, so: ** we declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); ** we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); ** we add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function that we implement separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() which is called when your state machine goes to the 'checkEndOfGame' state, you check for this variable and for other 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * We catch the score notification on the client side in gomoku.js->setupNotifications() and we set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * We implement the function declared to handle the notification. notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] b15b86e48944c4f923af7ef297aa6a697eeeb1d3 394 393 2012-12-06T22:09:13Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * we declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * we add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function that we implement separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() which is called when your state machine goes to the 'checkEndOfGame' state, you check for this variable and for other 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * We catch the score notification on the client side in gomoku.js->setupNotifications() and we set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * We implement the function declared to handle the notification. notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 44aa0b4f13643a40db740d0e30d5fba5828e6493 395 394 2012-12-06T22:10:32Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * we declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * we add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function that we implement separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, you check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * We catch the score notification on the client side in gomoku.js->setupNotifications() and we set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * We implement the function declared to handle the notification. notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 52031b9a65d85144687c385bb98224a18b1c90ae 396 395 2012-12-06T22:12:25Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in gomoku.js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 77087421e739e44ab260cdb86601e5a2c38b6915 397 396 2012-12-06T22:15:10Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in gomoku.game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in gomoku.js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] e369ecff0c1d3a7db33d7af073473f038c059ff9 398 397 2012-12-06T22:16:01Z Een 3 /* Setup the board */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections (jstpl_intersection). <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in gomoku.game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in gomoku.js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 2a35d8692c6958f159f309735364afa225f17b63 399 398 2012-12-06T22:17:16Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit gomoku.game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit gomoku.game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit gomoku.js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in gomoku.game.php->getAllDatas() is now available in your gomoku.js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your gomoku.js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your gomoku.game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In gomoku.game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In gomoku.js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in gomoku.game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in gomoku.js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding gomoku.js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in gomoku.action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in gomoku.game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in gomoku.js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in gomoku.js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in gomoku.game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in gomoku.game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in gomoku.game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in gomoku.game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in gomoku.game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in gomoku.js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] bfb8e63de4f2e6318889b6e22a427809c8949c3c 400 399 2012-12-06T22:19:30Z Een 3 wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created : [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 54ee41a684ba1db25a6c94e6715491f67ca872f8 Studio FAQ 0 53 363 347 2012-12-05T21:45:22Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. cb1693b12ae4ed239fcfaa093165d65ff0849426 390 363 2012-12-06T21:43:08Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. 49c74b0ae1da806be0026784581066e1b1ae1b64 Tiedosto:Gomoku stones.png 6 80 372 2012-12-05T22:45:06Z Een 3 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tutorial reversi 0 57 376 323 2012-12-05T23:05:05Z Pikiou 1872 Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_disc" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 5735d4c608381a182395d0aeed7517711422fc58 378 376 2012-12-05T23:10:00Z Pikiou 1872 Buggy code result of mixing the words disc and token wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${xy}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { xy: x+''+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+''+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+''+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". c86f6bd8839862875261fc3c1f99193e43decdc8 379 378 2012-12-05T23:11:55Z Pikiou 1872 Buggy code because of a missing underscore between x and y wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one database entry for each square, with a "NULL" value which mean "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we active the first player to make it active at the beginning of the game. Now, we need to make these tokens appears on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that format the result of this SQL query in a PHP array with x, y and player attribute. The last thing we need to do is to process this array on client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". e10e837e5c2636c2340994157b3085c572f3e307 380 379 2012-12-06T00:36:07Z Pikiou 1872 /* Setup the initial game position */ Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 5c64283860e84e2f59acc0cd203849a21a95e15b 381 380 2012-12-06T01:12:25Z Pikiou 1872 /* Setup the initial game position */ $blackplayer_id and $whiteplayer_id weren't defined wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 97a7cd5873575fc05bf2f8f58afb59ef3699ee58 382 381 2012-12-06T01:20:35Z Pikiou 1872 /* The discs */ Set player colors to white and black wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi game, it's very simple although. Here's a diagram of our game state machine for Reversi: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in our reversi.game.php file the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 1506c28901430d0ff115464f36c7b1e6868aa63f 383 382 2012-12-06T02:07:54Z Pikiou 1872 /* The game state machine */ Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it. At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 798fcf2511667129243ba3665423ad2815dbad92 384 383 2012-12-06T12:08:18Z Pikiou 1872 /* Display allowed moves */ Missing the possibleMove class CSS declaration wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves that our PHP "updatePossibleMoves" create for us, and add the "possibleMove" class to corresponding square. Finally, we use BGA framework "addTooltipToClass" method to associate a tooltip to all these highlighted squares in order players can understand the meaning of this. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 39ed1b8df7b3fc58dc4c7a6c1fe1286413cab9bc 385 384 2012-12-06T12:11:08Z Pikiou 1872 /* Display allowed moves */ Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 56px; height: 56px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". fe70922d87a5d2f33217e98ae8fb82131665a115 386 385 2012-12-06T12:28:33Z Pikiou 1872 /* Make the squares appears */ Squares didn't fill their space wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * On server side, check the move is correct, apply Reversi rules and jump to next player. * On client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone click on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to active the next player when we enter in the "nextPlayer" game state: <pre> function stNextPlayer() { // Active next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 37dba66a4ed6f61ec2a854bcc3923bbe996220a7 387 386 2012-12-06T15:30:58Z Pikiou 1872 /* Let's play */ Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 156ea2f58083036772ef8343548e257ae8345b4a 388 387 2012-12-06T15:32:18Z Pikiou 1872 /* Let's play */ Need to initialize statistics wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appears automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handle the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Made these discs blinking and set them to the specified color for( var i in notif.args.turnedOver ) { var disc = notif.args.turnedOver[ i ]; // Make the disc blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ), dojo.fadeOut( { node: 'disc_'+disc.x+''+disc.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'disccolor_000000', 'disccolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'disccolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'disc_'+disc.x+''+disc.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of it. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 0618aa74ff1170f381a45a8f753790836619cea2 389 388 2012-12-06T16:33:49Z Pikiou 1872 /* Make the move appears automatically */ Typos wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "stats.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 9bf25b0bffa92efc21acecec6a36e5d946859991 Studio back-office 0 56 391 313 2012-12-06T21:43:49Z Een 3 /* Sources */ wikitext text/x-wiki You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. === Sources === Contains a form for committing your sources to the BGA repository. * Game name field should contain the name of your game (lower case, no space). Ex: puertorico * Comment field should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error happening at the table you are currently playing at. ; Current table request logs : this log traces all the actions happening at the table you are currently playing at. ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. b8fd0f6cc336100886af11fe1c0b50fea185e534 Studio function reference 0 55 392 327 2012-12-06T22:06:45Z Een 3 /* Client side (Javascript functions) */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 6d38dcd8680be3c28de97befb1efdaa8f385d027 Tutorial gomoku 0 73 401 400 2012-12-06T22:19:41Z Een 3 /* You will start from our 'emtpy game' template */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 751fd879e8fc20a5a4276b292a84a8fc69aeed72 402 401 2012-12-06T22:21:47Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 87d10ec1ed913f5e43d8a32370d7bda0e373fadf 403 402 2012-12-06T22:22:43Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get after some tweaks and refresh: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 4e63521137d4911761e2ace1450021690f87d429 404 403 2012-12-06T22:23:06Z Een 3 /* Setup the backbone of your game */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states. One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 87d10ec1ed913f5e43d8a32370d7bda0e373fadf 405 404 2012-12-06T22:24:28Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * we init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] d1f9ba2385a1a391c47d8959c71dba650410cd96 406 405 2012-12-06T22:26:48Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, notify it to update the score on the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] aacabbe7efb7c6b91947fe7a33b4e170d580f067 407 406 2012-12-06T22:27:38Z Een 3 /* Implement rules and end of game conditions */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1000 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 'auto' ); })); slide.play(); // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, send a score update notification to the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 5ffdfba9f0e591c1b7eee1236e59ba7b7ffc2c94 Studio 0 49 408 343 2012-12-06T22:29:11Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2f629611da109a5f3e8c61e0d395592c19b646ef 409 408 2012-12-07T10:09:19Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 4776ee490903437bd94367e54ca55ff5d0e80636 414 409 2012-12-11T14:48:31Z Een 3 /* How do I register? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is this? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] f2d26be5ae879cac19e56ee406d83528b3d57077 428 414 2013-01-05T09:35:34Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == How can I develop a board game on BGA Studio? == Check these presentations first: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] a91516e964e65f1efa5a28e4ab7e4121bf38ea02 429 428 2013-01-05T09:41:36Z Sourisdudesert 1 /* How can I develop a board game on BGA Studio? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How do I register? == Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] dc62fcaa145e23c1d275111856dc5c7d56a8afb7 430 429 2013-01-05T09:46:29Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] aa67d4007c5a725783c814eefd8d15c3745f1878 432 430 2013-01-05T09:47:50Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == Other resources == [[Studio FAQ]] [[Studio file reference]] [[Studio function reference]] [[Studio back-office]] [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] e2f3e477cb0aa6465e28ac5c6fbd0159b5cddd76 433 432 2013-01-05T09:55:12Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] a08a63f342a76db9ad2d456591e2f4759344c61f 434 433 2013-01-05T09:58:34Z Sourisdudesert 1 /* BGA Studio Framework reference */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|Files structure of a BGA game]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] e6490a1a2089cdb7871765132f3f8d702ac1853c 435 434 2013-01-05T09:59:06Z Sourisdudesert 1 /* BGA Studio Framework reference */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] d3b8d6a65251dce3e3e8ceb78bfa73c42efb0f65 436 435 2013-01-05T10:08:56Z Sourisdudesert 1 /* BGA Studio Framework reference */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== [[Main game logic (<gamename>.game.php)]] [[Your game state machine (states.inc.php)]] [[Game database model (dbmodel.sql)]] [[Players actions (<gamename>.action.php)]] [[Game material description (material.inc.php)]] [[Game statistics (stats.inc.php)]] ==== Game interface ==== [[Game interface logic (<gamename>.js)]] [[Game art (img directory)]] [[Game interface stylesheet (<gamename>.css)]] [[Game layout: view and template (<gamename>.view.php and <gamename>_<gamename>.tpl)]] ==== Other components === [[Game options and preferences (gameoptions.inc.php)]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] f40cffdcec66a058431e166fc222e144c5b19136 437 436 2013-01-05T10:09:07Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== [[Main game logic (<gamename>.game.php)]] [[Your game state machine (states.inc.php)]] [[Game database model (dbmodel.sql)]] [[Players actions (<gamename>.action.php)]] [[Game material description (material.inc.php)]] [[Game statistics (stats.inc.php)]] ==== Game interface ==== [[Game interface logic (<gamename>.js)]] [[Game art (img directory)]] [[Game interface stylesheet (<gamename>.css)]] [[Game layout: view and template (<gamename>.view.php and <gamename>_<gamename>.tpl)]] ==== Other components ==== [[Game options and preferences (gameoptions.inc.php)]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] aefa09e482bda70b699e7e2e8febb638d76e4ebc 438 437 2013-01-05T10:10:34Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic (yourgamename.game.php)]] * [[Your game state machine (states.inc.php)]] * [[Game database model (dbmodel.sql)]] * [[Players actions (yourgamename.action.php)]] * [[Game material description (material.inc.php)]] * [[Game statistics (stats.inc.php)]] ==== Game interface ==== * [[Game interface logic (yourgamename.js)]] * [[Game art (img directory)]] * [[Game interface stylesheet (yourgamename.css)]] * [[Game layout: view and template (yourgamename.view.php and yourgamename_yourgamename.tpl)]] ==== Other components ==== * [[Game options and preferences (gameoptions.inc.php)]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 1072c45c47be8bd6020ce2a38ed8a472b8cc97cf 441 438 2013-01-05T10:14:00Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic (yourgamename.js)]] * [[Game art (img directory)]] * [[Game interface stylesheet (yourgamename.css)]] * [[Game layout: view and template (yourgamename.view.php and yourgamename_yourgamename.tpl)]] ==== Other components ==== * [[Game options and preferences (gameoptions.inc.php)]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] fd2c9b3cfb81e63f498e7363e2a6550c66091a2f 450 441 2013-01-05T10:17:16Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Game options and preferences: gameoptions.inc.php]] [[Studio function reference]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 62bbce3b9b3b136dadd05103b019629bcaac908f Studio FAQ 0 53 410 390 2012-12-09T20:19:37Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the Developer tools with Chrome which has about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the save/restore functionality (NB: currently non functional, will be ok with the next studio update). It enable you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. ef1e7e780ff9d182e7f6232aae6cca4f1616f718 411 410 2012-12-09T20:21:23Z Een 3 /* What is the best way to debug? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the Developer tools with Chrome which has about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality (NB: currently non functional, will be ok with the next studio update). It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. f65463a81a57517a503e0f7b4adc233b9926ba58 412 411 2012-12-09T20:42:02Z Een 3 /* What is the best way to debug? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the Developer tools with Chrome which has about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. a244da1f57822595825408622baa1c79d71d759f 413 412 2012-12-09T20:46:36Z Een 3 /* What is the best way to debug? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. 4a1d7d389aee2ef5066258585ecfc2056b52cde0 420 413 2012-12-23T17:17:40Z Een 3 /* What is the best way to debug? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389: Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. dd8456a862c8ae6069b019e08227068406386654 421 420 2012-12-23T17:18:00Z Een 3 /* Some frequent errors */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 03c40861e6f13964b3afa0ce1e9f0d483e12882a The Boss 0 46 415 217 2012-12-15T07:48:27Z Sweetnaty 1920 /* Description */ wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! '''Цель игры.''' 1. В конце раунда игрок с наибольшим количеством гангстеров в городе выигрывают карту города, расположенную рубашкой вверх (лицом в низ. 2. В конце игры выигрывают игроки которые больше всего заработали миллионов. ''' Описание''' В каждом городе, кроме Чикаго, имеется карта расположенная рубашкой вверх. Остальная часть карт распределена между игроками. Каждый игрок может видеть цвета карт других участников. '''Раунд:''' 1. Расположить гангстеров в городе (большие кубики – «гангстеры – эксперты») они возвращаются обратно к игроку в конце раунда (если не были убиты, заключены под стражу или не попали в больницу). Маленькие кубики – «гангстеры- новички», в конце раунда не возвращаются. Чтобы выиграть карту города, ваших гангстеров в городе должно быть больше, чем у противников. 2. После расположения гангстеров в городе, вы должны обязательно выложить одну карту с вашей руки. На этом ваш ход заканчиватеся. Когда у каждого игрока остается по две карты на руках, открывается карта полиции. Игра заканчивается, если открыты карты полиции с тремя одинаковыми по цвету значками. ''Если вы выигрываете « пистолет», то ваш гангстер убит, если «решетка» – гангстер не участвует в игре в течение 2-ух раундов, если "больница"(красный крест) - гангстер не участвует в игре в течение 1 раунда. Если Вы получаете вычеркнутую карту человека в Цинциннати, вам запрещено располагать гангстеров в этом городе до конца игры.'' '''Карта Чикаго(Алькапоне).''' У Чикаго (Алькапоне) нет скрытой карты, вместо этого игрок, сыгравший гангстеров в Чикаго, делит выигрышные деньги из городов расположенных слева от него (Чикаго) и делит их с Алькапоне, с округлением в меньшую сторону. '''Подсказки новичкам.''' Гангстеров не обязательно выкладывать каждый раунд, обязательным является выложить карту города с вашей руки. Можно дождаться пока ваши противники начнут выкладывать карты с руки, чтобы понять, где можно заработать миллион. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. ec426d7a7f218f82ecde489ba149533dc36b1aa3 416 415 2012-12-15T07:48:54Z Sweetnaty 1920 /* Goal */ wikitext text/x-wiki '''Цель игры.''' 1. В конце раунда игрок с наибольшим количеством гангстеров в городе выигрывают карту города, расположенную рубашкой вверх (лицом в низ. 2. В конце игры выигрывают игроки которые больше всего заработали миллионов. ''' Описание''' В каждом городе, кроме Чикаго, имеется карта расположенная рубашкой вверх. Остальная часть карт распределена между игроками. Каждый игрок может видеть цвета карт других участников. '''Раунд:''' 1. Расположить гангстеров в городе (большие кубики – «гангстеры – эксперты») они возвращаются обратно к игроку в конце раунда (если не были убиты, заключены под стражу или не попали в больницу). Маленькие кубики – «гангстеры- новички», в конце раунда не возвращаются. Чтобы выиграть карту города, ваших гангстеров в городе должно быть больше, чем у противников. 2. После расположения гангстеров в городе, вы должны обязательно выложить одну карту с вашей руки. На этом ваш ход заканчиватеся. Когда у каждого игрока остается по две карты на руках, открывается карта полиции. Игра заканчивается, если открыты карты полиции с тремя одинаковыми по цвету значками. ''Если вы выигрываете « пистолет», то ваш гангстер убит, если «решетка» – гангстер не участвует в игре в течение 2-ух раундов, если "больница"(красный крест) - гангстер не участвует в игре в течение 1 раунда. Если Вы получаете вычеркнутую карту человека в Цинциннати, вам запрещено располагать гангстеров в этом городе до конца игры.'' '''Карта Чикаго(Алькапоне).''' У Чикаго (Алькапоне) нет скрытой карты, вместо этого игрок, сыгравший гангстеров в Чикаго, делит выигрышные деньги из городов расположенных слева от него (Чикаго) и делит их с Алькапоне, с округлением в меньшую сторону. '''Подсказки новичкам.''' Гангстеров не обязательно выкладывать каждый раунд, обязательным является выложить карту города с вашей руки. Можно дождаться пока ваши противники начнут выкладывать карты с руки, чтобы понять, где можно заработать миллион. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 881a4b4353af0bd885bae8ed8e22b845fd106ad1 417 416 2012-12-15T07:49:42Z Sweetnaty 1920 /* Beginner Tips */ wikitext text/x-wiki '''Цель игры.''' 1. В конце раунда игрок с наибольшим количеством гангстеров в городе выигрывают карту города, расположенную рубашкой вверх (лицом в низ. 2. В конце игры выигрывают игроки которые больше всего заработали миллионов. ''' Описание''' В каждом городе, кроме Чикаго, имеется карта расположенная рубашкой вверх. Остальная часть карт распределена между игроками. Каждый игрок может видеть цвета карт других участников. '''Раунд:''' 1. Расположить гангстеров в городе (большие кубики – «гангстеры – эксперты») они возвращаются обратно к игроку в конце раунда (если не были убиты, заключены под стражу или не попали в больницу). Маленькие кубики – «гангстеры- новички», в конце раунда не возвращаются. Чтобы выиграть карту города, ваших гангстеров в городе должно быть больше, чем у противников. 2. После расположения гангстеров в городе, вы должны обязательно выложить одну карту с вашей руки. На этом ваш ход заканчиватеся. Когда у каждого игрока остается по две карты на руках, открывается карта полиции. Игра заканчивается, если открыты карты полиции с тремя одинаковыми по цвету значками. ''Если вы выигрываете « пистолет», то ваш гангстер убит, если «решетка» – гангстер не участвует в игре в течение 2-ух раундов, если "больница"(красный крест) - гангстер не участвует в игре в течение 1 раунда. Если Вы получаете вычеркнутую карту человека в Цинциннати, вам запрещено располагать гангстеров в этом городе до конца игры.'' '''Карта Чикаго(Алькапоне).''' У Чикаго (Алькапоне) нет скрытой карты, вместо этого игрок, сыгравший гангстеров в Чикаго, делит выигрышные деньги из городов расположенных слева от него (Чикаго) и делит их с Алькапоне, с округлением в меньшую сторону. '''Подсказки новичкам.''' Гангстеров не обязательно выкладывать каждый раунд, обязательным является выложить карту города с вашей руки. Можно дождаться пока ваши противники начнут выкладывать карты с руки, чтобы понять, где можно заработать миллион. b47bbe214e6ce4ab1cc0aa683a747d92216769da 418 417 2012-12-15T18:40:21Z Sweetnaty 1920 wikitext text/x-wiki '''Цель игры.''' 1. В конце раунда игрок с наибольшим количеством гангстеров в городе выигрывают карту города, расположенную рубашкой вверх (лицом вниз). 2. В конце игры выигрывают игроки которые больше всего заработали миллионов. ''' Описание''' В каждом городе, кроме Чикаго, имеется карта расположенная рубашкой вверх. Остальная часть карт распределена между игроками. Каждый игрок может видеть цвета карт других участников. '''Раунд:''' 1. Расположить гангстеров в городе (большие кубики – «гангстеры – эксперты») они возвращаются обратно к игроку в конце раунда (если не были убиты, заключены под стражу или не попали в больницу). Маленькие кубики – «гангстеры- новички», в конце раунда не возвращаются. Чтобы выиграть карту города, ваших гангстеров в городе должно быть больше, чем у противников. 2. После расположения гангстеров в городе, вы должны обязательно выложить одну карту с вашей руки. На этом ваш ход заканчиватеся. Когда у каждого игрока остается по две карты на руках, открывается карта полиции. Игра заканчивается, если открыты карты полиции с тремя одинаковыми по цвету значками. ''Если вы выигрываете « пистолет», то ваш гангстер убит, если «решетка» – гангстер не участвует в игре в течение 2-ух раундов, если "больница"(красный крест) - гангстер не участвует в игре в течение 1 раунда. Если Вы получаете вычеркнутую карту человека в Цинциннати, вам запрещено располагать гангстеров в этом городе до конца игры.'' '''Карта Чикаго(Алькапоне).''' У Чикаго (Алькапоне) нет скрытой карты, вместо этого игрок, сыгравший гангстеров в Чикаго, делит выигрышные деньги из городов расположенных слева от него (Чикаго) и делит их с Алькапоне, с округлением в меньшую сторону. '''Подсказки новичкам.''' Гангстеров не обязательно выкладывать каждый раунд, обязательным является выложить карту города с вашей руки. Можно дождаться пока ваши противники начнут выкладывать карты с руки, чтобы понять, где можно заработать миллион. 45d5f6a85beada1c1bf36cf6ec5b930d4354e8f2 419 418 2012-12-15T18:41:21Z Sweetnaty 1920 wikitext text/x-wiki '''Цель игры.''' 1. В конце раунда игрок с наибольшим количеством гангстеров в городе выигрывают карту города, расположенную рубашкой вверх (лицом вниз). 2. В конце игры выигрывают игроки которые больше всего заработали миллионов. ''' Описание'''''' ''' В каждом городе, кроме Чикаго, имеется карта расположенная рубашкой вверх. Остальная часть карт распределена между игроками. Каждый игрок может видеть цвета карт других участников. '''Раунд:''' 1. Расположить гангстеров в городе (большие кубики – «гангстеры – эксперты») они возвращаются обратно к игроку в конце раунда (если не были убиты, заключены под стражу или не попали в больницу). Маленькие кубики – «гангстеры- новички», в конце раунда не возвращаются. Чтобы выиграть карту города, ваших гангстеров в городе должно быть больше, чем у противников. 2. После расположения гангстеров в городе, вы должны обязательно выложить одну карту с вашей руки. На этом ваш ход заканчиватеся. Когда у каждого игрока остается по две карты на руках, открывается карта полиции. Игра заканчивается, если открыты карты полиции с тремя одинаковыми по цвету значками. ''Если вы выигрываете « пистолет», то ваш гангстер убит, если «решетка» – гангстер не участвует в игре в течение 2-ух раундов, если "больница"(красный крест) - гангстер не участвует в игре в течение 1 раунда. Если Вы получаете вычеркнутую карту человека в Цинциннати, вам запрещено располагать гангстеров в этом городе до конца игры.'' '''Карта Чикаго(Алькапоне).''' У Чикаго (Алькапоне) нет скрытой карты, вместо этого игрок, сыгравший гангстеров в Чикаго, делит выигрышные деньги из городов расположенных слева от него (Чикаго) и делит их с Алькапоне, с округлением в меньшую сторону. '''Подсказки новичкам.''' Гангстеров не обязательно выкладывать каждый раунд, обязательным является выложить карту города с вашей руки. Можно дождаться пока ваши противники начнут выкладывать карты с руки, чтобы понять, где можно заработать миллион. fda07a0572c0515408272f1bd3015ebe97a36b9f 423 419 2012-12-30T18:59:04Z Spacediver 718 reverted English back, moved Russian into corresponding separate page. wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space left each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 216df9ddb16c37d4791196cf9503d42bd9c7b533 Gamehelpgomoku 0 81 422 2012-12-24T12:55:06Z Een 3 Created page with " == Goal == Create an unbroken row of exactly five stones of your color horizontally, vertically, or diagonally. Overlines (rows of 6 stones or more) do not count. == Vari..." wikitext text/x-wiki == Goal == Create an unbroken row of exactly five stones of your color horizontally, vertically, or diagonally. Overlines (rows of 6 stones or more) do not count. == Variant == In Gomoku+ (also named Caro) in order to win your row of five stones must be free at both ends. This balances the game differently and gives more defensive power to the white player. == Openings == Black always play first. With the 'standard' (free) opening, black has got a distinct advantage over white who is directly put 'under attack'. The 'tournament' opening aims to even the odds between the black and white players: * black lays the first stone at the center of the board * white lays the second stone in one of the spaces adjoining to the center of the board (horizontally, vertically or diagonally) * black lays the third stone at least three spaces away from the center of the board. 7a98f64e6b90bd76c2333efa14892ecb86a538ec Keskustelu käyttäjästä:Sweetnaty 3 82 424 2012-12-30T19:01:39Z Spacediver 718 Перевод описания игры ;) wikitext text/x-wiki == Перевод описания игры ;) == Русский перевод описания игры нужно делать не поверх английского, который открывается отсюда: http://en.boardgamearena.com/#!gamepanel?game=theboss ...а на отдельной странице русского описания, куда ведет ссылка с '''русской версии''' сайта (обрати внимание на URL) http://ru.boardgamearena.com/#!gamepanel?game=theboss ca8e0ebb2778d155cb96b4f1de657321806eb7db Gamehelphawaii 0 34 425 101 2013-01-01T20:20:23Z Tobylongbeach 477 wikitext text/x-wiki Overview: You are the chieftain of a kingdom (your placemat) with up to five villages (rows). You buy or go to the islands to get place tiles that you put in your villages. There are five rounds and you get a decreasing amount of resources each round with which to acquire place tiles. Its a many-paths-to-victory kind of game. (It has been described as Stone Age without the dice). There are three kinds of resources: 1) Feet - let you move around the board, or travel to the islands or go to the cove to catch fish. 2) Clam shells - let you buy place tiles from the board 3) Fruit - can be used in place of feet or clam shells in all-or-nothing fashion. If you substitute for feet, you must substitute the entire moving cost. If you substitute for clams, you must substitute the entire purchase price. Moving: Your chieftains start at the beach and it costs nothing to return there from the board. Each movement in from the beach costs one foot. Moving up, down, right, left or diagonal on the board costs one foot per space. Staying on a space costs one foot. To go to the islands, you use your boats and feet. The closest island costs three feet and the farthest costs six. You have to have the feet as well as the space on the boats for the feet. You get an immediate victory point bonus for going to the islands but you do not get a price token (more about that later). Also, you get the level 2 version of whatever place tile or item you are getting on the islands. For instance, to go to the six foot island, you would need either: A) two three-feet boats and six feet resources or B) you would need the four-foot boat (which comes with a prepaid foot) and the two-foot boat as well as five feet resources. That is the advantage of the pre-paid foot on the four-foot boat. == Purchasing: == To buy some thing from the board: Select the price token (round colored circle with number inside) on the space with the place tile or item you want. If there is more than one place tile available on that space, you will be given a choice of which one you want. The movement cost will be in feet and the purchase price will be in clam shells but you can substitute either or both with fruit if you have it. You gain the price token for the round. There must be an available price token in order to purchase something from that space. You can pay double to get the level 2 version, which for most tiles is double effect. You place it in the village (row) of your choice. You must be able to place it in order to purchase it. A) You can only have one of each type of place tile in each village. For instance, you cannot have a foot (1) hut and a foot (2) hut in the same village but you can have a coconut fruit (1) tree and a breadfruit (2) tree in the same village. B) You can have only one god in each village. You can only have one of each type of god in your whole kingdom and the god vp effects are kingdom-wide. B) Each village must begin with a hut. The huts have a picture of a hut on them: foot hut, clam hut, spear hut, exchange hut, and long hut. ''' == Resources == ''' == Base reources == Base resources are on the round indicator. They decrease with each succeeding round. Everyone gets the base resources at the beginning of the round. Any unused resources can be used in the next round. == In-play resource bonuses == *The third, fourth and fifth villages give you 1, 2 and 3 clams respectively when you purchase Kahunas (Hawaiian priests) for them. *When you purchase for your kingdom the second, third and fourth Tikis, you are granted 1, 2, and 3 feet, respectively. *One or two of the island treasures are four fruit resources. == Resource Generators: == generate resources at the beginning of the next round. *Fruit level ones - one fruit *Fruit level twos - two fruit *Clam hut level one - one clam *Clam hut level two - two clams *Foot hut level one - one foot *Foot hut level two - two feet *Irrigation level two - one resource of your choice *Ku god - level one and two - one foot *Kane god level one - one clam *Kane god level two - two clams == Converter huts == Whenever you spend resources, a level 1 converter hut allows you to substitute one resource of your choice. So, for instance, you could move one space and buy something that has four price token on it with four fruit for the token and one clamshell being substituted for the one foot movement. Another example, you could go to the first island for two feet and one clamshell using a level 1 boat. The converter level 2 hut is the same but with any two resources. '''== Victory Points ==''' == In-play VP's: == *Going to the islands gives you a vp bonus. *Some of the treasures on the islands are 5 vp's. *Spear huts and the Ku god give you a bonus whenever you take a price token with crossed spears. Level 1 is 1 pt. bonus, Level 2 is two pt. bonus. == Round-End VP's: == There is a large sheild on the round indicator at the bottom. If the sum of price tokens and fish are greater than the number on the sheild, then you get a bonus. There is a large bonus for the player with highest sum score, a moderate bonus for the second highest and a token bonus for everyone else whose sum was greater than the sheild. Surfer place tiles lower the sheild value for qualifying for the bonus but surfers do not contribute to your sum when comparing to other players. Lono god level 1 gives you 2 vp's if you qualify for the bonus, and Lono god level 2 gives you 4 vp's if you qualify for the bonus == Game-End VP's: == A) Each village whose size does not extend to the Tikis is ignored for end-game scoring - its place tiles do not count and neither does its Kahuna. The villages below it are not affected - each village must reach the Kahunas independently. B) The Kahunas on the first, second, third, fourth and fifth villages scores the player five, five, ten, ten and fifteen points respectively. B) A hula dancer in a village scores one point for each place tile in her village including herself. A level 2 hula dancer scores two points instead of one for each place tile in her village including herself. C) Irrigation gives you pts for each type of fruit you have in that particular village: Breadfruit, guava, coconuts and bananas. D) The Long Hut level 2 is worth five points. The long hut level one just takes up space making it easier to reach the Kahunas but does not, itself, score points. E) The Laka god in your kingdom scores one pt for each fruit symbol on your place tiles in all of the villages which is the whole kingdom. Laka god level 2 scores two points for each fruit symbol. F) Kanaloa god scores two pts for each boat and each surfer on all the villages in the kingdom. Kanaloa 2 scores 4 pts for each boat and surfer. 5948df612fd3dc390051357ccb7e029823e4562b Browser support 0 13 426 69 2013-01-03T16:00:30Z Een 3 wikitext text/x-wiki [[Category:Help]] <h3>For the best experience, we recommend:</h3> * '''Google Chrome 10+''' [http://www.google.com/chrome Windows] [http://www.google.com/chrome?platform=mac Mac] [http://www.google.com/chrome?platform=linux Linux] * '''Mozilla Firefox 4+''' [http://www.mozilla.org/products/firefox/ Windows] [http://www.mozilla.org/products/firefox/ Mac] [http://www.mozilla.org/products/firefox/ Linux] Board Game Arena takes advantage of the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download or update, no plugins to install, etc. Consequently, to play you need to use a modern web browser. Generally speaking, the more recent your web browser is, the more pleasant your game experience on Board Game Arena will be. This website makes intensive use of Javascript and your browser's graphics capabilities. Thus, if you want to have the best gaming experience with Board Game Arena, you should use one of the browsers listed above. However, we officially support the following browsers: * Google Chrome 4+ * Mozilla Firefox 3.5+ * Internet Explorer 9+ * Safari 4+ (Note: We do NOT support playing on iPads or similar tablets.) 82c74a446ab46ee91347f67d008b263af608ce10 Help 0 4 427 219 2013-01-05T09:30:25Z Sourisdudesert 1 wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena]] ** [[About us]] ** [[Club Board Game Arena]] ** [[Contact us]] === Detailed help === * [[Getting started]] * [[Referral]] * [[Browser support]] * [[Moderation and grades]] * [[Game clock]] * [[Rating]] * [[Reputation]] * [[Translation guidelines]] c6067b5fc3f21568031e1b54885fd097134d4f1d How to join BGA developer team? 0 83 431 2013-01-05T09:46:40Z Sourisdudesert 1 Created page with " Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to b..." wikitext text/x-wiki Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, from time to time you will get by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) <pre>NB: during the BETA pĥase, to smooth things up and make sure that everything is working correctly, we will work only with 3 fearless developers on 3 simple games.</pre> Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! e2db83f30a3fec64e81365b9198379fb7aaf63b7 Studio file reference 0 54 439 319 2013-01-05T10:11:33Z Sourisdudesert 1 /* states.inc.php */ wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game: * game_box.png is displayed on the main site on the game description page and when creating a table (280x280 px) * game_icon.png is the icon displayed in the lists of games and tables (50x50 px) * publisher.png is the logo of the publisher of the game, displayed on the game description page (width: 150 px) * and other images that you need. You should use [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites] for better web performance. === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. 8c614009c3b9805382342cdc1cc704652e8f0897 Players actions (yourgamename.action.php) 0 84 440 2013-01-05T10:12:58Z Sourisdudesert 1 Created page with "=== APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $defaul..." wikitext text/x-wiki === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> b249dbfc2f08f19e51c808b946b0ee96fbb467e6 Players actions: yourgamename.action.php 0 85 442 2013-01-05T10:14:07Z Sourisdudesert 1 Created page with "=== APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $defaul..." wikitext text/x-wiki === APP_GameAction class (<gamename>.action.php) === ; function isArg( $argName ) : Is this argument filled ? ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : Get script argument with the correct type : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> define( 'AT_int', 0 ); // an integer define( 'AT_posint', 1 ); // a positive integer define( 'AT_float', 2 ); // a float define( 'AT_email', 3 ); // an email define( 'AT_url', 4 ); // a URL define( 'AT_bool', 5 ); // 1/0/true/false define( 'AT_enum', 6 ); // argTypeDetails list the possible values define( 'AT_alphanum', 7 ); // only 0-9a-zA-Z_ and space </pre> b249dbfc2f08f19e51c808b946b0ee96fbb467e6 Studio function reference 0 55 443 392 2013-01-05T10:14:23Z Sourisdudesert 1 /* APP_GameAction class (.action.php) */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 67089d3595e14018b2123d14b1494161851413cb 445 443 2013-01-05T10:14:52Z Sourisdudesert 1 /* Table class (.game.php) */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 44a0e60d2e0d37b86e80e3dd236f2f8fbe58c8fd 448 445 2013-01-05T10:16:23Z Sourisdudesert 1 /* Exceptions you can throw */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 2a86068880a0d99bb4bbd5114e55f46dc395295c Main game logic: yourgamename.game.php 0 86 444 2013-01-05T10:14:46Z Sourisdudesert 1 Created page with "=== Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; functio..." wikitext text/x-wiki === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row d6b0fc4adabe456d434028c91896088ba3d49559 446 444 2013-01-05T10:15:22Z Sourisdudesert 1 wikitext text/x-wiki === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. cc95d4afc675e2d76a97502cc585135882ee4467 Järjestelmäviesti:Sidebar 8 2 447 2 2013-01-05T10:16:09Z Sourisdudesert 1 wikitext text/x-wiki * navigation ** http://boardgamearena.com|BoardGameArena.com ** mainpage|mainpage-description ** Studio|BGA Studio doc ** recentchanges-url|recentchanges * SEARCH * TOOLBOX * LANGUAGES c14629314b9ab77a3a7e98dd4e81d67ff8b7634e Game interface logic (yourgamename.js) 0 87 449 2013-01-05T10:16:36Z Sourisdudesert 1 Created page with "== Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the t..." wikitext text/x-wiki == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 1e8dc993d4a1c11e472e5365ef1cd2b1e2306264 Game interface logic: yourgamename.js 0 88 451 2013-01-05T10:17:58Z Sourisdudesert 1 Created page with "; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; thi..." wikitext text/x-wiki ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player f579fca77b6768cc083f8382df27a363a405db43 Studio 0 49 452 450 2013-01-05T10:19:35Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Game options and preferences: gameoptions.inc.php]] === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2e931b3065efa50525fe88c1fe3cb0780242e182 470 452 2013-01-11T12:06:01Z Sourisdudesert 1 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Game options and preferences: gameoptions.inc.php]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] c9cc10a2bb0d2ebc0e9a8423b365bf4d2c928557 501 470 2013-01-11T15:47:24Z Sourisdudesert 1 /* Other components */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 0b06c55548d666ef11e610ff3549d651ed1dafa2 Studio file reference 0 54 453 439 2013-01-05T10:20:18Z Sourisdudesert 1 /* 'img' directory */ wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game. === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. 2e7104613bcd7d665c1d222af10c97e42d262d56 454 453 2013-01-05T10:20:49Z Sourisdudesert 1 /* 'img' directory */ wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game (the game art). === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. d8bd4830bfb0b86dcdf637549a560f991d2667ba Game art: img directory 0 89 455 2013-01-05T10:38:20Z Sourisdudesert 1 Created page with "== Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (28..." wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript methods "dontPreloadImage" and "ensureSpecificImageLoading" (see [[Game_interface_logic:_yourgamename.js]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. * and other images that you need. You should use [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites] for better web performance. 1dc31974112f183a7fcaed36d1d945a3cc155fd2 456 455 2013-01-05T10:38:52Z Sourisdudesert 1 /* Images loading */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript methods "dontPreloadImage" and "ensureSpecificImageLoading" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. * and other images that you need. You should use [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites] for better web performance. a4fe8df99a6159ca229086d9623e012f0d009063 457 456 2013-01-05T10:41:44Z Sourisdudesert 1 /* Game art */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript methods "dontPreloadImage" and "ensureSpecificImageLoading" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] 0784d5f20fdd5fdc96ac6f303ff161afe79a2d9a 458 457 2013-01-05T10:43:51Z Sourisdudesert 1 /* Requested images */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. ;publisher2.png (optional) * If the game has been co-published by 2 publishers, you should upload a second image named "publisher2.png" (same characteristic than the first one). == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript methods "dontPreloadImage" and "ensureSpecificImageLoading" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] d01797346a998de4ef982255fe6e442d5ac27e44 Main game logic: yourgamename.game.php 0 86 459 446 2013-01-05T10:51:52Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value ; function DbQuery( $sql ) : Executes sql query on the database ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for the sql query. First column must be a primary or alternate key. The resulting collection can be empty. ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found : Raise an exception if more than 1 row is returned ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 2e0422c01375873d889cd852153941009f5dee2c 460 459 2013-01-05T11:06:45Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; function DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" <pre> Example 1: self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) Example 2: self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0' ), 1235 => 'myuser1' ) ) </pre> ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. fd806d41060480c3e3ad5ff086652fbc30b52eea 461 460 2013-01-05T11:07:26Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; function DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; function getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; function getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0' ), 1235 => 'myuser1' ) ) </pre> ; protected function getNonEmptyCollectionFromDB( $sql ) : Idem, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql query as an associative array or null if there is no result : Raise an exception if the query return more than one row ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 86bb80f233b8a2c1bced9630000c454879ac6000 462 461 2013-01-05T11:10:05Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. e0229a8a72f18d407c46e8f9b0f6ced331099012 463 462 2013-01-05T11:10:28Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. === Table class (<gamename>.game.php) === ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) ; function getPlayersNumber() : Returns the number of players playing at the table ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value === Exceptions you can throw === ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 755a3c27dc759d73a4c99b8e9b888c680c3350d1 464 463 2013-01-05T11:15:56Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; function getPlayersNumber() : Returns the number of players playing at the table ; function getActivePlayerId() : Get the "active_player", whatever what is the current state type : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated. It is not always the active player. ; function getCurrentPlayerName() : Get the "current_player" name ; function getCurrentPlayerColor() : Get the "current_player" color ; function isCurrentPlayerZombie() : Check the "current_player" zombie status == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 417c04fb7af0bff492989ae695f4f345a37c52e4 465 464 2013-01-05T11:19:55Z Sourisdudesert 1 /* Accessing player informations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; function getActivePlayerName() : Get the "active_player" name ; function getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; function getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; function getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; function isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. b4e9344ba235bca69d92731c2f926fbeb2959325 466 465 2013-01-05T11:23:52Z Sourisdudesert 1 /* Accessing player informations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : TODO ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 2d204c8553c8d00246f0efaab8c18e8a447179e5 467 466 2013-01-10T16:57:45Z Pikiou 1872 /* Accessing player informations */ alternative for getPlayersNumber in setupNewGame wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : TODO ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 013a1ae0c029d6b1be62ea12e8269bbe471a6ccd 468 467 2013-01-11T11:48:42Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : TODO ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 92a6616477137f9c24f2625d6ca7f4c362b5c522 469 468 2013-01-11T11:51:35Z Sourisdudesert 1 /* Accessing player informations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 8319df29226e7701f278f183dfafd66173d508ce 487 469 2013-01-11T14:18:47Z Sourisdudesert 1 /* Accessing player informations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active ; function activePrevPlayer() : Make the previous player active == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. d1370aa7c9c631a4b646a39a8b9a3232b9202c2a 488 487 2013-01-11T14:22:33Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see [[Editing Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 8ea47e5ee7ae9faad7a436fc0ba0812140c00fb4 491 488 2013-01-11T14:41:59Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 0f0aee686502df2f0e731c6f48979a9e608eb757 498 491 2013-01-11T15:33:44Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; function DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; function DbAffectedRow() : Return the number of row affected by the last operation ; function escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. f5a3c57d55530f819dccea62b703ca23316dd8af 499 498 2013-01-11T15:42:31Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; function checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 3837cd00d2194d98c93a777e1a61827da5feec9b 500 499 2013-01-11T15:44:56Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. b71dd97bac1bd6a6e4f272c0e71c0c642f6f682c Your game state machine: states.inc.php 0 90 471 2013-01-11T13:19:50Z Sourisdudesert 1 Created page with " This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the..." wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === Game state ID === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). 64db0b4c31e2f6372cd2819e12291f99087f1d58 472 471 2013-01-11T13:24:31Z Sourisdudesert 1 /* Syntax */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === Game state ID === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === Game state name === (mandatory) The name of a game state is used to identify it in your game logic. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> 122046b59cce388a4eb0e91e6b1ee6ef5669b9f9 473 472 2013-01-11T13:24:58Z Sourisdudesert 1 /* Game state name */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === Game state ID === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === Game state name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> 2807b405f3ea0817378ad5eea0e0e6019aff05dc 474 473 2013-01-11T13:39:01Z Sourisdudesert 1 /* Syntax */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> == description == (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. == descriptionmyturn == 984ab22f16888cc5be11c4feefebe2f1cb87832f 475 474 2013-01-11T13:43:11Z Sourisdudesert 1 /* descriptionmyturn */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> == description == (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. == descriptionmyturn == (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. c71d493e9411d9543bfdc4e12064b01331340977 476 475 2013-01-11T13:43:42Z Sourisdudesert 1 /* description */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. == descriptionmyturn == (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. 90f150994f2c80467670000ba070796d005fc402 477 476 2013-01-11T13:43:48Z Sourisdudesert 1 /* descriptionmyturn */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. 9be143bed66f07c9026f63c1d288e56f45502de9 478 477 2013-01-11T13:45:32Z Sourisdudesert 1 /* Syntax */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. 8a60a37723221c9a982b73cb792a1a9ed9a8eabd 479 478 2013-01-11T13:51:07Z Sourisdudesert 1 /* descriptionmyturn */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". e53759ccf506d389b182eb85ab80aa719bc7599c 480 479 2013-01-11T13:52:11Z Sourisdudesert 1 /* id */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". 0f7c457ab6a3eacff262fd685514839a3fa65e8b 481 480 2013-01-11T13:52:19Z Sourisdudesert 1 /* id */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". c42e3bcd700318400aafdc3b1e13b1afe4bdd309 482 481 2013-01-11T14:02:57Z Sourisdudesert 1 /* action */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> 6350bda2becff3188000260128af9be79098886d 483 482 2013-01-11T14:07:59Z Sourisdudesert 1 /* transitions */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) { return ; } .... </pre> a60bd8c9f68bcf0ec45770c0f3a5b39cf652b9f0 484 483 2013-01-11T14:08:14Z Sourisdudesert 1 /* possibleactions */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> a54b5a8b71b94de9489c426b6e3e7f8d8c17399b 485 484 2013-01-11T14:13:58Z Sourisdudesert 1 /* possibleactions */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "placeWorkers", "description" => clienttranslate('${actplayer} must place some workers'), "descriptionmyturn" => clienttranslate('${you} must place some workers'), "type" => "activeplayer", '''"args" => "argPlaceWorkers",''' "possibleactions" => array( "placeWorkers" ), "transitions" => array( "nextPlayer" => 11, "nextPhase" => 12, "zombiePass" => 11 ) ), </pre> === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. ab7a9a311b70804a2341683b23ee13310f5b188f 486 485 2013-01-11T14:16:16Z Sourisdudesert 1 /* args */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "arg" PHP method (see below "arg" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "arg" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "placeWorkers", "description" => clienttranslate('${actplayer} must place some workers'), "descriptionmyturn" => clienttranslate('${you} must place some workers'), "type" => "activeplayer", "args" => "argPlaceWorkers", <================================== HERE "possibleactions" => array( "placeWorkers" ), "transitions" => array( "nextPlayer" => 11, "nextPhase" => 12, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlaceWorkers » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also used values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. 1cbe5d759b0e2ab2ccc6de088662afa6e71c75b6 Game database model: dbmodel.sql 0 92 490 2013-01-11T14:41:22Z Sourisdudesert 1 Created page with "In this file you specify the database schema of your game. This file contains SQL queries that will be executed during the creation of your game table. Note: you can't chang..." wikitext text/x-wiki In this file you specify the database schema of your game. This file contains SQL queries that will be executed during the creation of your game table. Note: you can't change the database schema during the game. == Create your schema == To build this file, we recommend you to build the tables you need with the PhpMyAdmin tool (see BGA user guide), and then to export them and to copy/paste the content inside this file. == Default tables == Important: by default, BGA creates 4 tables for your game: global, stats, gamelog, and player. You must not modify the schema of global, stats and gamelog tables (and you must not access them directly with SQL queries in your PHP code). You may add columns to "player" table. This is very practical to add simple values associated with players. Example: <pre> ALTER TABLE `player` ADD `player_reserve_size` SMALLINT UNSIGNED NOT NULL DEFAULT '7'; </pre> For your information, the useful columns of default "player" table are: * player_no: the index of player in natural playing order. * player_id * player_name: (note: you should better access this date with getActivePlayerName() or loadPlayersBasicInfos() methods) * player_score: the current score of the player (displayed in the player panel). You must update this field to update player's scores. * player_score_aux: the secondary score, used as a tie breaker. You must update this field according to tie breaking rules of the game. 2a8820a0ff219470e0d437ff49a161ce7e12ae0f Players actions: yourgamename.action.php 0 85 492 442 2013-01-11T14:56:04Z Sourisdudesert 1 wikitext text/x-wiki == Purpose of this file == With this file, you define all the players entry points (ie: possible game actions) of your game. This file is a sort of "bridge" between the AJAX calls you are doing from your Javascript client side, and your main PHP code in "yourgame.game.php". The role of the methods defined in this file is to filter the arguments, eventually to format them a little bit, and then to call a corresponding PHP method from your main game logic ("yourgame.game.php" file). Methods in this file must be short: no game logic must be introduced here. == Example of typical action method == (from Reversi example) <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> == Methods to use in action methods == ; function setAjaxMode : Must be use at the beginning of each action method. ; function ajaxResponse : Must be use at the end of each action method. ; function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false ) : This method must be used to retrieve the arguments sent with your AJAX query. : You must NOT use "_GET", "_POST" or equivalent PHP variables to do this, as it is unsafe. : This method use the following arguments: : * argName: the name of the argument to retrieve. : * argType: the type of the argument. You should use one of the following: : 'AT_int' for an integer : 'AT_posint' for a positive integer : 'AT_float' for a float : 'AT_bool' for 1/0/true/false : 'AT_enum' for an enumeration (argTypeDetails list the possible values as an array) : 'AT_alphanum' for a string with 0-9a-zA-Z_ and space : bCanFail means than a validation failure is possible (user input) : The main argType values are as follows. <pre> ; function isArg( $argName ) : Return "true" or "false" whether "argName" has been specified as an argument of the AJAX request or not. </pre> 8c4f55762d427d4ec9ec448bb25c0a942246d00f 493 492 2013-01-11T14:58:20Z Sourisdudesert 1 /* Methods to use in action methods */ wikitext text/x-wiki == Purpose of this file == With this file, you define all the players entry points (ie: possible game actions) of your game. This file is a sort of "bridge" between the AJAX calls you are doing from your Javascript client side, and your main PHP code in "yourgame.game.php". The role of the methods defined in this file is to filter the arguments, eventually to format them a little bit, and then to call a corresponding PHP method from your main game logic ("yourgame.game.php" file). Methods in this file must be short: no game logic must be introduced here. == Example of typical action method == (from Reversi example) <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> == Methods to use in action methods == '''function setAjaxMode''' Must be use at the beginning of each action method. '''function ajaxResponse''' Must be use at the end of each action method. '''function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false )''' This method must be used to retrieve the arguments sent with your AJAX query. You must NOT use "_GET", "_POST" or equivalent PHP variables to do this, as it is unsafe. This method use the following arguments: * argName: the name of the argument to retrieve. * argType: the type of the argument. You should use one of the following: 'AT_int' for an integer 'AT_posint' for a positive integer 'AT_float' for a float 'AT_bool' for 1/0/true/false 'AT_enum' for an enumeration (argTypeDetails list the possible values as an array) 'AT_alphanum' for a string with 0-9a-zA-Z_ and space 'AT_numberlist' for a list of several numbers separated with "," or ";" (ex: exemple: 1,4;2,3;-1,2). bCanFail means than a validation failure is possible (user input) The main argType values are as follows. '''function isArg( $argName )''' Return "true" or "false" whether "argName" has been specified as an argument of the AJAX request or not. </pre> a19d6dfedf8fe2277f7069fe3b0a5a9c9a7646dd 494 493 2013-01-11T15:03:50Z Sourisdudesert 1 /* Methods to use in action methods */ wikitext text/x-wiki == Purpose of this file == With this file, you define all the players entry points (ie: possible game actions) of your game. This file is a sort of "bridge" between the AJAX calls you are doing from your Javascript client side, and your main PHP code in "yourgame.game.php". The role of the methods defined in this file is to filter the arguments, eventually to format them a little bit, and then to call a corresponding PHP method from your main game logic ("yourgame.game.php" file). Methods in this file must be short: no game logic must be introduced here. == Example of typical action method == (from Reversi example) <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> == Methods to use in action methods == '''function setAjaxMode''' Must be use at the beginning of each action method. '''function ajaxResponse''' Must be use at the end of each action method. '''function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false )''' This method must be used to retrieve the arguments sent with your AJAX query. You must NOT use "_GET", "_POST" or equivalent PHP variables to do this, as it is unsafe. This method use the following arguments: * argName: the name of the argument to retrieve. * argType: the type of the argument. You should use one of the following: 'AT_int' for an integer 'AT_posint' for a positive integer 'AT_float' for a float 'AT_bool' for 1/0/true/false 'AT_enum' for an enumeration (argTypeDetails list the possible values as an array) 'AT_alphanum' for a string with 0-9a-zA-Z_ and space 'AT_numberlist' for a list of several numbers separated with "," or ";" (ex: exemple: 1,4;2,3;-1,2). * mandatory: specify "true" if the argument is mandatory. * default: if mandatory=false, you can specify here a default value in case the argument is not present. * argTypeDetails: see AT_enum above. * bCanFail: if true, specify that it may be possible that the argument won't be of the type specified by argType (and then do not log this as a fatal error in the system, and return a standard exception to the player). '''function isArg( $argName )''' This is a useful method when you only want to check if an argument is present or not present in your AJAX request (and don't care of the value. It returns "true" or "false" whether "argName" has been specified as an argument of the AJAX request or not. 46067eef7c4401ee68e1c3a4512ab090091e4c1d 495 494 2013-01-11T15:07:01Z Sourisdudesert 1 /* Methods to use in action methods */ wikitext text/x-wiki == Purpose of this file == With this file, you define all the players entry points (ie: possible game actions) of your game. This file is a sort of "bridge" between the AJAX calls you are doing from your Javascript client side, and your main PHP code in "yourgame.game.php". The role of the methods defined in this file is to filter the arguments, eventually to format them a little bit, and then to call a corresponding PHP method from your main game logic ("yourgame.game.php" file). Methods in this file must be short: no game logic must be introduced here. == Example of typical action method == (from Reversi example) <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> == Methods to use in action methods == '''function setAjaxMode''' Must be use at the beginning of each action method. '''function ajaxResponse''' Must be use at the end of each action method. '''function getArg( $argName, $argType, $mandatory=false, $default=NULL, $argTypeDetails=array(), $bCanFail=false )''' This method must be used to retrieve the arguments sent with your AJAX query. You must NOT use "_GET", "_POST" or equivalent PHP variables to do this, as it is unsafe. This method use the following arguments: * argName: the name of the argument to retrieve. * argType: the type of the argument. You should use one of the following: 'AT_int' for an integer 'AT_posint' for a positive integer 'AT_float' for a float 'AT_bool' for 1/0/true/false 'AT_enum' for an enumeration (argTypeDetails list the possible values as an array) 'AT_alphanum' for a string with 0-9a-zA-Z_ and space 'AT_numberlist' for a list of several numbers separated with "," or ";" (ex: exemple: 1,4;2,3;-1,2). * mandatory: specify "true" if the argument is mandatory. * default: if mandatory=false, you can specify here a default value in case the argument is not present. * argTypeDetails: see AT_enum above. * bCanFail: if true, specify that it may be possible that the argument won't be of the type specified by argType (and then do not log this as a fatal error in the system, and return a standard exception to the player). '''function isArg( $argName )''' This is a useful method when you only want to check if an argument is present or not present in your AJAX request (and don't care of the value. It returns "true" or "false" whether "argName" has been specified as an argument of the AJAX request or not. == Useful tip: retrieve a list of number == If your Javascript send a list of integer separated by ";" (ex: "1;2;3;4") as an argument, you can transform them in a PHP array with the following: <pre> public function playCards() { self::setAjaxMode(); $card_ids_raw = self::getArg( "card_ids", AT_numberlist, true ); // Removing last ';' if exists if( substr( $card_ids_raw, -1 ) == ';' ) $card_ids_raw = substr( $card_ids_raw, 0, -1 ); if( $card_ids_raw == '' ) $card_ids = array(); else $card_ids = explode( ';', $card_ids_raw ); $this->game->playCards( $card_ids ); self::ajaxResponse( ); } </pre> bae1cc6ec52c6feadc80d3bd3e4aa03ff95908b8 Game material description: material.inc.php 0 93 496 2013-01-11T15:18:46Z Sourisdudesert 1 Created page with "This PHP file describes all the material of your game. This file is include by the constructor of your main game logic (yourgame.game.php), and then the variables defined her..." wikitext text/x-wiki This PHP file describes all the material of your game. This file is include by the constructor of your main game logic (yourgame.game.php), and then the variables defined here are accessible everywhere in your game logic file. Example from "Hearts": if you define this in material.inc.php: <pre> $this->colors = array( 1 => array( 'name' => clienttranslate('spade'), 'nametr' => self::_('spade') ), 2 => array( 'name' => clienttranslate('heart'), 'nametr' => self::_('heart') ), 3 => array( 'name' => clienttranslate('club'), 'nametr' => self::_('club') ), 4 => array( 'name' => clienttranslate('diamond'), 'nametr' => self::_('diamond') ) ); </pre> ... you can access "$this->colors" everywhere in your PHP game logic file. 076a3890e55f1fa9e5466f29cde040c46ffe67c0 497 496 2013-01-11T15:23:52Z Sourisdudesert 1 wikitext text/x-wiki This PHP file describes all the material of your game. This file is include by the constructor of your main game logic (yourgame.game.php), and then the variables defined here are accessible everywhere in your game logic file. Using material.inc.php makes your PHP logic file smaller and clean. Example from "Hearts": if you define this in material.inc.php: <pre> $this->colors = array( 1 => array( 'name' => clienttranslate('spade'), 'nametr' => self::_('spade') ), 2 => array( 'name' => clienttranslate('heart'), 'nametr' => self::_('heart') ), 3 => array( 'name' => clienttranslate('club'), 'nametr' => self::_('club') ), 4 => array( 'name' => clienttranslate('diamond'), 'nametr' => self::_('diamond') ) ); </pre> ... you can access "$this->colors" everywhere in your PHP game logic file. e7a93d7e58d52c2a8bef0f954da5a495b33020f5 Translations 0 94 502 2013-01-11T15:47:33Z Sourisdudesert 1 Created page with " (todo)" wikitext text/x-wiki (todo) 5d301b12a9331eb33c9c5d7c0fa41fb6c5d4ba56 503 502 2013-01-11T15:47:46Z Sourisdudesert 1 wikitext text/x-wiki ; function _( $text ) : Transparent function, used to mark strings to be translated on the server side (ex: error message) ; function clienttranslate( $string ) : Transparent function: used to mark string to be translated on client side (ex: notification message) 24c00b9c260eebd9acf6ee6ac66fb2fb049781c3 530 503 2013-01-15T16:55:04Z Sourisdudesert 1 wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". * On "material.inc.php", when defining texts for game material that must be displayed on client side. * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. 7faacf163abbdcae5aa87a9f68ef1964d1c12b63 531 530 2013-01-15T16:57:24Z Sourisdudesert 1 /* On server side (PHP) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". * On "material.inc.php", when defining texts for game material that must be displayed on client side. * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php aef6fc156d64ae4fb1e6e7bcdc3e4b83b5165666 532 531 2013-01-15T17:03:59Z Sourisdudesert 1 /* On server side (PHP) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 519d1b8d8f83dc3f8ef485ff25657a06063da52e 533 532 2013-01-15T17:04:47Z Sourisdudesert 1 /* On server side (PHP) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 9315062d846ea128dd8a5731f15b03d70346e96e 534 533 2013-01-15T17:07:34Z Sourisdudesert 1 /* On server side (PHP) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php b0251a213ab68551932ef7c5129f4d59dce827af Main game logic: yourgamename.game.php 0 86 504 500 2013-01-11T15:47:57Z Sourisdudesert 1 /* Translations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 852a46f013f70307a38382fd2ee5c3d0d18c797e 505 504 2013-01-11T15:49:19Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Using globals == == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 8d73b6994cd02253b43a940b0d520f77233a8f8c 506 505 2013-01-11T15:58:07Z Sourisdudesert 1 /* Using globals */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 636e63e3bc63774d2554983bed02eb9b115cf506 508 506 2013-01-11T16:03:04Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''createNextPlayerTable( $players, $bLoop=true )''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> $players = self::loadPlayersBasicInfos() $nextPlayers = createNextPlayerTable( $players ); // return array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); $nextPlayers = createNextPlayerTable( $players, false ); // return array( 1 => 2, 2 => 3, 0 => 1 ); </pre> == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 1d8d43d07f3ec3d264df38a6db890b7754487155 509 508 2013-01-11T16:06:05Z Sourisdudesert 1 /* Players turn order */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 5cda35c29ef2339660b1b9ef0cac8c40f36762c1 510 509 2013-01-11T16:06:52Z Sourisdudesert 1 /* Players turn order */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player (standard extra time is a game option) == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 08c40fe97a1b285f7b648813931d0f4fed42c8d4 511 510 2013-01-11T16:08:42Z Sourisdudesert 1 /* Reflexion time */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == == Game statistics == ; function initStat( $table_or_player, $name, $value, $player_id=null ) : Create a statistic entry for the specified statistics with a default value : In case of a "player" entry, if player_id is not specified, all players are set to the same value ; function setStat( $value, $name, $player_id = null ) : Set statistic value ; function incStat( $delta, $name, $player_id = null ) : Increment (or decrement) specified value == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. b916b96471dd091a82c47c1a595813f6813fe122 513 511 2013-01-11T16:15:56Z Sourisdudesert 1 /* Game statistics */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 30e4fbe4a7a9f3484375ac3e49c4de8ffb430c28 514 513 2013-01-11T16:24:11Z Sourisdudesert 1 /* Notify players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. c736610fc329a786629ddf77ed3612ca3a6e0400 515 514 2013-01-11T16:26:41Z Sourisdudesert 1 /* Notify players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; function activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; function activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 64e2d717a68cd4e452a230b8dd290fc7c24a1779 516 515 2013-01-11T16:29:48Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 0c9f99bbd1eec9cc35913e05464c1cab169d9918 517 516 2013-01-11T16:38:09Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. ; throw new BgaSystemVisibleException ( $error_message) : Same as previous, except that the message is visible by the user. You can use this if the message is understandable by the user. 3602613dae96f6572bb8b2b05719baaf61a71a93 518 517 2013-01-11T16:45:57Z Sourisdudesert 1 /* Managing errors and exceptions */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 7669befb882381077e8b9eead19707ff5f3d32b6 Help 0 4 507 427 2013-01-11T16:02:09Z Een 3 wikitext text/x-wiki An issue? A question? * [[Faq|Frequently asked questions]] could probably help you. * [http://forum.boardgamearena.com Forums] are also helpful to get information. == Help contents == * [[Faq|Frequently asked questions]] * [[About Board Game Arena|About Board Game Arena]] ** [[About us|About us]] ** [[Club Board Game Arena|Club Board Game Arena]] ** [[Contact us|Contact us]] === Detailed help === * [[Getting started|Getting started]] * [[Referral|Referral]] * [[Browser support|Browser support]] * [[Moderation and grades|Moderation and grades]] * [[Game clock|Game clock]] * [[Rating|Rating]] * [[Reputation|Reputation]] * [[Translation guidelines|Translation guidelines]] 401acc1457522cd40f7eeb302f5488ea375cbacb Game statistics: stats.inc.php 0 95 512 2013-01-11T16:11:38Z Sourisdudesert 1 Created page with " (todo)" wikitext text/x-wiki (todo) 144a80e923365406965058d618e72ad9edcf31e0 519 512 2013-01-11T16:52:46Z Sourisdudesert 1 wikitext text/x-wiki (to be completed) => see comments in your stats.inc.php file a01448ec26bcc5d5e1468c200ce2385a6c5e3145 Game interface stylesheet: yourgamename.css 0 96 520 2013-01-11T17:05:10Z Sourisdudesert 1 Created page with "This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourga..." wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('../../img/emptygame/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('../../img/emptygame/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. e3a85e1f26e247f89f549b3427b62770dd4ba20f How to join BGA developer team? 0 83 521 431 2013-01-13T12:07:41Z Een 3 /* Ok, I registered, what do I get? */ wikitext text/x-wiki Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf 'terms & conditions' document]. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: 'I agree with the terms & conditions for developers on BGA Studio joined as an attachment'. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, we'll discuss by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! 93291e486ca904e0dbed17dcadb4a04271acd827 Game interface logic: yourgamename.js 0 88 522 451 2013-01-14T16:02:38Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: TODO == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 2d591248b137f4f336137161c6c0d8e6fadc5784 523 522 2013-01-14T16:22:11Z Sourisdudesert 1 /* Dojo framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player f958116cefb92037d99b5803494b0e77dc5933bb 524 523 2013-01-14T16:28:08Z Sourisdudesert 1 /* Dojo framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' TODO '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player dfcc464befa61dd55fbf2d1dbfbbedc4f8e8ad8c 525 524 2013-01-14T16:46:51Z Sourisdudesert 1 /* Dojo framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player e4a4a9ede4662eeb6aba9c6aeacf08c55cfd91d0 526 525 2013-01-14T17:21:17Z Sourisdudesert 1 /* Dojo framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 07e0e96af372326eb1ccde8fd9b13758b8e25c31 527 526 2013-01-14T17:23:33Z Sourisdudesert 1 /* Dojo framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player df3c2d0d3f9af0259ea4c3a8c70edf7422cad672 528 527 2013-01-14T17:27:28Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == == Access and manipulate the DOM == == Animations == == Players input == (dojo.connect/checkAction/ajaxcall/confirmationDialog) == Notifications == == Tooltips == == BGA GUI components == == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 808f170e17bf773369f5fa182d57e47ad36472f3 535 528 2013-01-15T17:28:55Z Sourisdudesert 1 /* Useful methods from the BGA framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == == Access and manipulate the DOM == == Animations == == Players input == (dojo.connect/checkAction/ajaxcall/confirmationDialog) == Notifications == == Tooltips == == BGA GUI components == == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 5a821d5a7574d0e44e801b374da78484bf054ee8 536 535 2013-01-15T17:30:17Z Sourisdudesert 1 /* General tips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Access and manipulate the DOM == == Animations == == Players input == (dojo.connect/checkAction/ajaxcall/confirmationDialog) == Notifications == == Tooltips == == BGA GUI components == == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. Here's the list of the most useful methods you can use for your game interface: '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 500ffcf842c54b3d538b5b5bf0b09e97d468de80 537 536 2013-01-15T17:39:45Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == == BGA GUI components == '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 5925d7515da319684b74df69c34f3c44b7701ac4 538 537 2013-01-15T17:40:11Z Sourisdudesert 1 /* Useful methods from the BGA framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == == BGA GUI components == '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 75121e76f663a1539f180b725447d90adeecd4d5 539 538 2013-01-15T17:40:21Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == == BGA GUI components == '''dojo animations''' See "animations" below. '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. == Useful methods from the BGA framework == ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 50f6adb55d71b5fce9f62374c9ab7d9d4861f745 540 539 2013-01-15T17:40:39Z Sourisdudesert 1 /* BGA GUI components */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == == BGA GUI components == == Useful methods from the BGA framework == ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 90a053e179d91c2b7422976bf185e9f9717fdf62 541 540 2013-01-15T17:41:24Z Sourisdudesert 1 /* Useful methods from the BGA framework */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == == BGA GUI components == == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player b8f913e1bc2aa33d972f84996dd998cd11cd3cf2 542 541 2013-01-15T17:41:32Z Sourisdudesert 1 /* Tooltips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player e9c7a0524cff660e9ecc2cec157ee42827a9d76e 543 542 2013-01-15T17:41:53Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' TODO '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 21cd7cce3f7b0eebdab0b1b4bc17374c27817254 544 543 2013-01-15T17:42:27Z Sourisdudesert 1 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 9c7028944c23584ab2d72a72d9594fd21bb286e0 545 544 2013-01-15T17:44:47Z Sourisdudesert 1 /* BGA GUI components */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface. [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 25369aba21f0c435dfa537d7ac4a2fc67358583a 546 545 2013-01-15T17:45:07Z Sourisdudesert 1 /* BGA GUI components */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 8e75a75f76580e2f9cb781117094561f3c640a9a 547 546 2013-01-15T17:45:19Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 53fd25d376669bf4a89e2f54ff0832e2129018b5 548 547 2013-01-15T17:45:33Z Sourisdudesert 1 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 7118f94dfa528ed0ff07820f66e558f73d350d66 549 548 2013-01-15T17:45:45Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 5b54585df2c6e96126c1a881c0cdf6a6410227b7 550 549 2013-01-15T17:45:57Z Sourisdudesert 1 /* Access and manipulate the DOM */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player d847a4298f2d48659e7abac127332ca299483882 Studio 0 49 529 501 2013-01-14T20:17:28Z Een 3 /* How to join BGA developer team? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 7e6d5b482e09c3b7b35b48a1b07e9ecd44c62b61 Referral 0 11 551 90 2013-01-18T13:35:33Z Een 3 /* How does this work? */ wikitext text/x-wiki [[Category:Help]] When new players discover Board Game Arena thanks to you, you become a member of [[Club Board Game Arena]] == How does this work? == Each time a new player creates an account on Board Game Arena from your referrer web address (see below), he becomes one of your '''referees'''. As soon as this referee plays 3 games, you become a member of BGA club. == How to get referees? == To recruit referees, use your personal referrer web address: {sponsorshipurl} New players MUST create an account from this web address to become one of your referees. == How to get many referees? == * [http://boardgamearena.com/#!doc/sponsor?facebook Tell my friends on Facebook] * [http://boardgamearena.com/#!doc/sponsor?twitter Tell my followers on Twitter] * [http://boardgamearena.com/#!doc/sponsor?email Tell my friends by email] * Publish my personal web address in some forum, send it by MSN, ... == How many BGA club membership days can I win? == As soon as your first referee finish his '''third''' game on Board Game Arena, you win a '''1 month''' BGA club membership. ''Note: your account upgrade will be effective within 24 hours.'' As soon as you get '''2 referees''' with at least '''4 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. As soon as you get '''3 referees''' with at least '''5 games''' logged on Board Game Arena, you win another '''1 month''' BGA club membership. ... and so on: as your referees number grows you can win many free memberships ! == Referees and multiple accounts == It is of course forbidden to create fake referee accounts to win free memberships. 9eb06a83374493fecff6c484c9c14cc24b2bea66 Studio FAQ 0 53 552 421 2013-01-19T13:24:35Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the file, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. b45616fb440438f17118e394471ad3458d8347a5 553 552 2013-01-19T13:24:53Z Een 3 /* I can't edit the file, it looks like they are readonly. What's happening? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files for my game, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. bf2990a44d913df03be605bae6635a5712829a0d 554 553 2013-01-19T13:25:09Z Een 3 /* I can't edit the files for my game, it looks like they are readonly. What's happening? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 28e961be7320545802f150b6304b957d62ffe350 573 554 2013-01-29T21:23:27Z Sourisdudesert 1 /* How can I provide translation in my language? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == Is there a special way to declare the strings that must be translated? == Yes. This declaration is made through transparent functions, that depend on the context. In javascript files, you should use _( 'My string to translate' ). In php files, you should use self::_( 'My string to translate' ) when the string can be translated on the server side (ex: title included in the game layout) and clienttranslate( 'My string to translate' ) when the string must be translated on the client side (ex: message for the game log). == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 228c35f8d6f017ce425e4137252e104afe9127d0 574 573 2013-01-29T21:23:43Z Sourisdudesert 1 /* Is there a special way to declare the strings that must be translated? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == It's pretty annoying to log in with multiple users to start a game. Is there some easier way? == You can use the 'Express start' function. It will automatically make the specified number of players join the game (using the first of your ten player accounts available) and start the game. During the game, there is a red arrow on the right of each player name, that you can use to open a tab from this player's perspective. You can also end the game in two clicks by clicking the 'End game' button then selecting 'Express game stop' in the popup. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. e36087c923ca5e0d25409eec810218f72e8c8f9f 576 574 2013-01-29T21:29:11Z Sourisdudesert 1 /* It's pretty annoying to log in with multiple users to start a game. Is there some easier way? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Is there a quick way to access the database for my current table? == Yes! While playing a game on studio, you have a "Go to game database" link at the bottom of your game. This link will bring you directly to the database for the current table. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 71a874d035ff4e8a5dc743da16fbb62b6344e8eb 580 576 2013-01-29T21:44:02Z Sourisdudesert 1 /* Is there a quick way to access the database for my current table? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the WinSCP client. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. c0989601e76d0ccf881330ba3f51785befb55fd5 581 580 2013-01-29T21:44:54Z Sourisdudesert 1 /* What should I use to access the files through SFTP? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 0c5d2fe1d5482e222473398fe6bd8f5bcdb945b0 582 581 2013-01-29T21:52:06Z Sourisdudesert 1 /* What is the best way to debug? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. d23029c5aaa00274a69aa2bd89e4b30be505699e Stock 0 97 555 2013-01-21T10:42:32Z Sourisdudesert 1 Created page with " "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Sto..." wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. aaa3491d40e9f58eb0c33a075306bbfaf932c973 556 555 2013-01-21T10:42:51Z Sourisdudesert 1 wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. (TODO) 259bdd5d57b95bb36ea7250c0d64f61c85daa016 557 556 2013-01-23T06:45:58Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. 596e2d334136943a584826bf495ae8cf793d5510 559 557 2013-01-26T04:58:23Z Pikiou 1872 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. d9a22a4260d98df2ad1fd5eefe00d825b91c94a7 Gamehelpintheyearofthedragon 0 51 558 227 2013-01-25T07:25:06Z Chefneil 2211 /* End-of-game Scoring */ wikitext text/x-wiki === End-of-round Scoring === * 1 point for each palace. * 1 point For each court lady. * 1 point For each privilege. (Small privilege = 1 point; Large privilege = 2 points) === End-of-game Scoring === * 2 points for each person tile. * For each monk: score [number of Buddhas] x [number of floors] points. * Each player sells back to the supply all of his rice and fireworks tiles for 2 yuan each.Afterward, each player earns 1 victory point for every 3 yuan he possesses. d14091f98a1adfed010f5ef619850821e1c47d1b Game interface logic: yourgamename.js 0 88 560 550 2013-01-29T20:16:07Z Sourisdudesert 1 /* Tooltips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: _ current game state & _ interface locking return true if action is authorized return false and display an error message if not (display no message if nomessage parameter is true) '''this.ajaxcall()''' TODO Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player cca113c65beb3f4b496694319e19911ae4adbf02 561 560 2013-01-29T20:23:45Z Sourisdudesert 1 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 7c1c1a5b909b7d3a6974119c27e7cb3648bd91d5 571 561 2013-01-29T21:21:55Z Sourisdudesert 1 /* Access and manipulate the DOM */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 89b39331514986dad84a1f8c6f5d7ad8ad561b3e 572 571 2013-01-29T21:22:21Z Sourisdudesert 1 /* Access and manipulate the DOM */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Notifications == == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 593b196f904b35192fcc41acc623cc34b71e88b9 Main game logic: yourgamename.game.php 0 86 562 518 2013-01-29T20:30:13Z Sourisdudesert 1 /* Translations */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 4d7d4744233cd8afd5bd1c1aa6db76ab3570a8d1 563 562 2013-01-29T20:38:07Z Sourisdudesert 1 /* Manage player scores and Tie breaker */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the following methods: ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 5fbea32b7752c400ce2e16ca6200144df516f94d 586 563 2013-01-29T22:10:38Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. IMPORTANT: BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 51b1fdd1370f93c3b2c675b52245872c70fa5754 587 586 2013-01-29T22:11:00Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 6502226df993ec63c7c972714147ec92d7471ffe 588 587 2013-01-29T22:11:07Z Sourisdudesert 1 /* Accessing database */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notification, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 6282c3822e4e213200925c1cc8200f6af6ed3443 589 588 2013-01-29T22:12:43Z Sourisdudesert 1 /* Notify players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. f2ac5c9d3fd78ef14e0c1ce46b4d58df770dcba0 590 589 2013-01-29T22:14:15Z Sourisdudesert 1 /* Managing errors and exceptions */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 1185bc779ecad94a1a233b0387229b9f919a6527 Game layout: view and template: yourgamename.view.php and yourgamename yourgamename.tpl 0 98 564 2013-01-29T20:53:10Z Sourisdudesert 1 Created page with "These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>,..." wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "reversi_reversi.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> ab1fa2414a3eb4c232aea1ee4034fcacd33db6a6 565 564 2013-01-29T20:59:20Z Sourisdudesert 1 wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "reversi_reversi.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == 1cbe6c9723a2d6bd6ac9ad8d975396e73c9c7645 566 565 2013-01-29T21:03:29Z Sourisdudesert 1 /* Blocks */ wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "reversi_reversi.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> be61b0d8430efd6be334f471987d1957b13d11e1 567 566 2013-01-29T21:03:49Z Sourisdudesert 1 wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> b72e8c036aae4063866997eef8e29091924aa682 568 567 2013-01-29T21:06:50Z Sourisdudesert 1 /* Blocks */ wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). 01e70246e836e8ea7fdc7274434f54f6b8027375 569 568 2013-01-29T21:15:22Z Sourisdudesert 1 /* Blocks */ wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). == Nested blocks == You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it: <pre> (In template file) <!-- BEGIN player --> <div class="miniboard" id="miniboard_{PLAYER_ID}"> <div class="card_places"> <!-- BEGIN card_place --> <div id="card_place_{PLAYER_ID}_{PLACE_ID}"> </div> <!-- END card_place --> </div> </div> <!-- END player --> (In view file) $this->page->begin_block( "mygame_mygame.tpl", "card_place" ); // Nested block must be declared first $this->page->begin_block( "mygame_mygame.tpl", "player" ); foreach( $players as $player_id => $player ) { // Important: nested block must be reset here, otherwise the second player miniboard will // have 8 card_place, the third will have 12 card_place, and so one... $this->page->reset_subblocks( 'card_place' ); for( $i=1; $i<=4; $i++ ) { $this->page->insert_block( "card_place", array( 'PLAYER_ID' => $player_id, 'PLACE_ID' => $i ); } $this->page->insert_block( 'player', array( 'PLAYER_ID' => $player_id ); } </pre> b6cd67e2292dd8601b8c6a6f00338aa000a8901b 570 569 2013-01-29T21:19:50Z Sourisdudesert 1 wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). == Nested blocks == You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it: <pre> (In template file) <!-- BEGIN player --> <div class="miniboard" id="miniboard_{PLAYER_ID}"> <div class="card_places"> <!-- BEGIN card_place --> <div id="card_place_{PLAYER_ID}_{PLACE_ID}"> </div> <!-- END card_place --> </div> </div> <!-- END player --> (In view file) $this->page->begin_block( "mygame_mygame.tpl", "card_place" ); // Nested block must be declared first $this->page->begin_block( "mygame_mygame.tpl", "player" ); foreach( $players as $player_id => $player ) { // Important: nested block must be reset here, otherwise the second player miniboard will // have 8 card_place, the third will have 12 card_place, and so one... $this->page->reset_subblocks( 'card_place' ); for( $i=1; $i<=4; $i++ ) { $this->page->insert_block( "card_place", array( 'PLAYER_ID' => $player_id, 'PLACE_ID' => $i ); } $this->page->insert_block( 'player', array( 'PLAYER_ID' => $player_id ); } </pre> == Javascript templates == For game elements that come and go from the game area, we suggest you to define a Javascript template. A Javascript template is defined in your template file like this: (Reversi Token from Reversi example): <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: a section for javascript templates is already available at the end of your template skeleton file. Then, you can use this javascript template to insert this piece of HTML in your game interface, like this: <pre> dojo.place( this.format_block( 'jstpl_disc', { xy: x+''+y, color: color } ) , 'discs' ); </pre> 9806b9dfa29915aec140a1017843edaa96b9cd79 Studio 0 49 575 529 2013-01-29T21:25:00Z Sourisdudesert 1 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 71afcb42b7cf6027b4c429ccf53881bf70a0dfc5 Tools and tips of BGA Studio 0 99 577 2013-01-29T21:31:32Z Sourisdudesert 1 Created page with "== Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 pla..." wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Input/Output debugging section == 4b27048eb61349acf7cff6a912e8cd7df429beee 578 577 2013-01-29T21:39:56Z Sourisdudesert 1 /* Input/Output debugging section */ wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. 607228e76ddee8545782d4adb3003f0e4507cbc9 579 578 2013-01-29T21:42:54Z Sourisdudesert 1 wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. == Run from the chat == On BGA Studio, you can directly run a PHP method from the chat. For example, if on your PHP you have this method: <pre> function giveMoneyToPlayer($player_id, $amount) { // Do some stuff } </pre> You can call this method directly from the chat like this: "giveMoneyToCurrentPlayer(2564,2)". 5830ee3dfd380fce44ae7cbc5223a65eb4e15b54 Practical debugging 0 100 583 2013-01-29T21:53:08Z Sourisdudesert 1 Created page with " This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Debu..." wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the Studio backoffice. == Debugging my PHP game logic (or my view) == Debugging PHP is most of the time easy == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. aa44fcaeaecdd7c548e30cbeacca25903f08191c 584 583 2013-01-29T21:54:35Z Sourisdudesert 1 /* Debugging my game when it cannot start */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Debugging PHP is most of the time easy == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 8d4dc06fe5c513252e4a5c75711404b51879ba28 585 584 2013-01-29T22:04:44Z Sourisdudesert 1 wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a "die('ok');" PHP statement right after the PHP I am developing/debugging. This way, the PHP request will stop immediately, nothing will be commited to the database, and the game initial situation will remains. == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. d8fb000e12a9e4fd6921ed6bf0c2012cc574c113 591 585 2013-01-29T22:18:09Z Sourisdudesert 1 /* Debugging my PHP game logic (or my view) */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the server side (PHP), you can use one of these: * die(var_dump( $variable_to_inspect ); * throw new BgaUserException(var_dump( $variable_to_inspect ); On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. In general for debugging, think of using the 'Save & restore state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. c603f203a8e5dfd46b7ecdaa18e21ff21f0dd7ec 592 591 2013-01-29T22:29:50Z Sourisdudesert 1 wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == === Browsers === To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tooltip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 0cafffec180d2a8f54155d7119ff55c2acbfe733 593 592 2013-01-29T22:30:46Z Sourisdudesert 1 /* Tools */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tooltip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. c223b0d05f92488930d83663bc3e2977ceac439f 594 593 2013-01-29T22:30:57Z Sourisdudesert 1 /* General tooltip for debugging */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. f446e2bbceb1f7a0c2685da8aa1eccacf627d645 595 594 2013-01-29T22:39:27Z Sourisdudesert 1 /* Debugging my HTML/CSS layout */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 09a63b6229f62026d949b7ede710534f5e0b36cc 596 595 2013-01-29T22:41:41Z Sourisdudesert 1 /* Debugging my HTML/CSS layout */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: * console.log( variable_to_inspect ); will give you the object structure of the variable in the Firebug console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. * alert( variable_to_inspect ); will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 4a98b70a461dfb2203e4ef49ab559c0fd396ff8f 597 596 2013-01-29T22:41:56Z Sourisdudesert 1 /* What is the best way to debug? */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 7acea4428a85c6fe896dc1ade348a17d3a5e9108 598 597 2013-01-29T22:48:50Z Sourisdudesert 1 /* Debugging my Javascript game interface logic */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == == What is the best way to debug? == On the client side (Javascript), we recommand installing Firebug for Firefox (or using the 'Developer tools' with Chrome that have about the same functionalities), then: === Some frequent errors === ; The following error occurs when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." : Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. 06380bd3a0144fcea81636911402c0ed7e3daeba 599 598 2013-01-29T22:49:08Z Sourisdudesert 1 /* What is the best way to debug? */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == b7e2f0def9efdc1c661b1f8a9e18e95986cf3546 600 599 2013-01-29T22:49:46Z Sourisdudesert 1 /* Some frequent errors */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. a275e649f9e2640946cd8398d7ec6b216a0bc8c7 Game database model: dbmodel.sql 0 92 601 490 2013-01-30T13:44:45Z Sourisdudesert 1 /* Default tables */ wikitext text/x-wiki In this file you specify the database schema of your game. This file contains SQL queries that will be executed during the creation of your game table. Note: you can't change the database schema during the game. == Create your schema == To build this file, we recommend you to build the tables you need with the PhpMyAdmin tool (see BGA user guide), and then to export them and to copy/paste the content inside this file. == Default tables == Important: by default, BGA creates 4 tables for your game: global, stats, gamelog, and player. You must not modify the schema of global, stats and gamelog tables (and you must not access them directly with SQL queries in your PHP code). You may add columns to "player" table. This is very practical to add simple values associated with players. Example: <pre> ALTER TABLE `player` ADD `player_reserve_size` SMALLINT UNSIGNED NOT NULL DEFAULT '7'; </pre> For your information, the useful columns of default "player" table are: * player_no: the index of player in natural playing order. * player_id * player_name: (note: you should better access this date with getActivePlayerName() or loadPlayersBasicInfos() methods) * player_score: the current score of the player (displayed in the player panel). You must update this field to update player's scores. * player_score_aux: the secondary score, used as a tie breaker. You must update this field according to tie breaking rules of the game (see also: [[Main_game_logic:_yourgamename.game.php#Manage_player_scores_and_Tie_breaker|Manage_player_scores_and_Tie_breaker]]) 55928f7428de62d98335489cb1b48e7a5e837ad4 Game layout: view and template: yourgamename.view.php and yourgamename yourgamename.tpl 0 98 602 570 2013-01-30T13:51:18Z Sourisdudesert 1 /* Javascript templates */ wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). == Nested blocks == You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it: <pre> (In template file) <!-- BEGIN player --> <div class="miniboard" id="miniboard_{PLAYER_ID}"> <div class="card_places"> <!-- BEGIN card_place --> <div id="card_place_{PLAYER_ID}_{PLACE_ID}"> </div> <!-- END card_place --> </div> </div> <!-- END player --> (In view file) $this->page->begin_block( "mygame_mygame.tpl", "card_place" ); // Nested block must be declared first $this->page->begin_block( "mygame_mygame.tpl", "player" ); foreach( $players as $player_id => $player ) { // Important: nested block must be reset here, otherwise the second player miniboard will // have 8 card_place, the third will have 12 card_place, and so one... $this->page->reset_subblocks( 'card_place' ); for( $i=1; $i<=4; $i++ ) { $this->page->insert_block( "card_place", array( 'PLAYER_ID' => $player_id, 'PLACE_ID' => $i ); } $this->page->insert_block( 'player', array( 'PLAYER_ID' => $player_id ); } </pre> == Javascript templates == For game elements that come and go from the game area, we suggest you to define a Javascript template. A Javascript template is defined in your template file like this: (Reversi Token from Reversi example): <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: a section for javascript templates is already available at the end of your template skeleton file. Then, you can use this javascript template to insert this piece of HTML in your game interface, like this: <pre> dojo.place( this.format_block( 'jstpl_disc', { xy: x+''+y, color: color } ) , 'discs' ); </pre> == How to access game information from .view.php? == From your .view.php, you can access the following: === Access current player id=== <pre> global $g_user; $current_player_id = $g_user->get_id(); </pre> === Access game object === In your view file, "$this->game" contains an instance of your main game class. Example: <pre> // Access to some game elements description described in your "material.inc.php": $my_cards_types = $this->game->card_types; // Access to any (public) method defined in my .game.php file: $result = $this->game->myMethod(); </pre> a1db3a68a1c6b558482b31c4766efcb3c6170a16 Translations 0 94 603 534 2013-01-30T14:00:36Z Sourisdudesert 1 /* Focus on translating notifications */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 00b876af979e40df1604bdaf14081b503740bd63 604 603 2013-01-30T14:08:44Z Sourisdudesert 1 /* WARNING: how to make sure your strings will be translated */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short string. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 6f088dd77b603414b4986ec781e0714c6b6ca381 605 604 2013-01-30T14:09:03Z Sourisdudesert 1 /* How to not make translators crazy ;) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short strings. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 5342d4acfd2ce7de3b9b024bdee754ec12cbe55f 606 605 2013-01-30T14:11:52Z Sourisdudesert 1 /* How to not make translators crazy ;) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short strings. * The BGA translation policy is to be flexible on grammar... We prefer to write "player gets 1 coin(s)" than write two versions of the same string for plural and singular - it reduces the number of strings to translate. == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 437b64de7275fe16f6a09023d10a92b7ab0303b2 Game interface logic: yourgamename.js 0 88 607 572 2013-01-30T14:12:52Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player f4a705f0f672ce8dddadba6c9f4d613dd937ae54 608 607 2013-01-30T14:21:47Z Sourisdudesert 1 /* Notifications */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 9a8fdfb42e8b8eb4ec315d1daf3cb4d45f9e680d 609 608 2013-01-30T14:27:59Z Sourisdudesert 1 /* Subscribe to notifications */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName == Animations == ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 4c7fc3933a944503c6d3bffcdd3a28fe399ce52c 610 609 2013-01-30T14:43:43Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player ceee517a59f3dc740a50da1f396cfc171f5e8cf3 611 610 2013-01-30T14:51:01Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 9b8fb6b0cdfd751a4df6d848620ba468d2c8bcc2 612 611 2013-01-30T14:52:08Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player ff91a04387ccb6ce0a83b2057d73d2f3b02aceb2 613 612 2013-01-30T14:58:55Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player f113c6f0688a5c8675268b06b776a472e8e296e6 614 613 2013-01-30T15:00:13Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 8a38d623f326ef8eba093681c87adc036cbbcc11 615 614 2013-01-30T15:07:09Z Sourisdudesert 1 /* Moving elements */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player f585ef408be1bd33f808044adf1ccf799dc50695 616 615 2013-01-30T15:08:50Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 8b08708d91b15ed204ebd2b648b54676117e1b7d 617 616 2013-01-30T15:08:59Z Sourisdudesert 1 /* General tips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 34566293e7a72a0b6122ff14fd675a9a43dc1aa2 618 617 2013-01-30T15:19:59Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 412a63f4c33407628ed1d60a616f31f7c8f1cc43 619 618 2013-01-30T15:20:36Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 1c8e0371e586802deb58127e413bdc5396e52fec 620 619 2013-01-30T15:20:52Z Sourisdudesert 1 /* Other useful stuff */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] b36c6cc0e7326219304189b189b0bc902479c611 621 620 2013-01-30T15:22:21Z Sourisdudesert 1 /* Dialogs, warning messages, confirmation dialogs, ... */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 26121d2968fe9d01fe23fe8daff1cf93e815eeaa 622 621 2013-01-30T15:30:44Z Sourisdudesert 1 /* Dialogs, warning messages, confirmation dialogs, ... */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] da65aad9c96ea34a94096025ba4eb7d0ade5f6d6 623 622 2013-01-30T15:38:14Z Sourisdudesert 1 /* Dialogs, warning messages, confirmation dialogs, ... */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: === Scoring dialogs === TODO == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 065726dd9b1f8325cdbbf4cceabb97f1869edd0a 624 623 2013-01-30T15:41:40Z Sourisdudesert 1 /* Dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); </pre> === Scoring dialogs === TODO == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] fb2368d30850f91de0df598317f17804ad420937 625 624 2013-01-30T15:43:40Z Sourisdudesert 1 /* Dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> === Scoring dialogs === TODO == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 4de87503c1d29276dafd667781f2d379528e7581 626 625 2013-01-30T15:45:40Z Sourisdudesert 1 /* Dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === TODO == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 65ee871aec9432a99069caac08f4127d8ca0d83d 627 626 2013-01-30T15:48:21Z Sourisdudesert 1 /* Scoring dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 370fe31d6e015e611d22df85d8b741b7c9e7ea13 628 627 2013-01-30T16:09:54Z Sourisdudesert 1 /* Scoring dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] 91ac3f1c2bed80d7d77eae390f6293ca4b66848f 629 628 2013-01-30T16:12:45Z Sourisdudesert 1 /* BGA GUI components */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 3cdee1e01b47aa1e56b73b29b4d01a823ae07118 644 629 2013-02-12T12:22:39Z Sourisdudesert 1 /* General tips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 4724100d9aa96ce01e65d3b388664799dde876b1 Practical debugging 0 100 630 600 2013-01-30T16:17:01Z Sourisdudesert 1 /* Some frequent errors */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. 8a99f335a3ab6c99af2563f71f88bdc43ea419d6 631 630 2013-01-30T16:31:34Z Sourisdudesert 1 /* Some frequent errors */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. === When I do a move, I got "Move recorded, waiting for update ..." forever === "Move recorded" means that your ajaxcall request has been sent to the server and returned normally. "Waiting for update" means that your client interface is waiting for some notifications from the server that correspond to the move we just did. If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code. === Some player action is triggered randomly when I click somewhere on the game area === You probably used "dojo.connect" on a null object. In this case, dojo.connect associate the event (ex: "onclick") to the whole game area. Most of the time it happens in this situation, when my_object element does not exists: <pre> dojo.connect( $("my_object"), "onclick", this, function() { ... } </pre> To determine if this is the case, place "alert( $("my_object") )" before the dojo.connect to check if the object exists or not. === Javascript does not know how to sum two numbers === Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers. As a result: <pre> var i=1; i += notif.args.increment; // With notif.args.increment='1' alert( i ); // i=11 instead of 2 !! Javascript concatenate 2 strings ! </pre> To solve this, to hesitate to use the "toint" function: <pre> var i=1; i += toint( notif.args.increment ); // With notif.args.increment='1' alert( i ); // i=2 :) </pre> 33d94ad6544cac38815e06aab6deb73b0cd2d016 632 631 2013-01-30T16:35:03Z Sourisdudesert 1 /* Javascript does not know how to sum two numbers */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. === When I do a move, I got "Move recorded, waiting for update ..." forever === "Move recorded" means that your ajaxcall request has been sent to the server and returned normally. "Waiting for update" means that your client interface is waiting for some notifications from the server that correspond to the move we just did. If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code. === Some player action is triggered randomly when I click somewhere on the game area === You probably used "dojo.connect" on a null object. In this case, dojo.connect associate the event (ex: "onclick") to the whole game area. Most of the time it happens in this situation, when my_object element does not exists: <pre> dojo.connect( $("my_object"), "onclick", this, function() { ... } </pre> To determine if this is the case, place "alert( $("my_object") )" before the dojo.connect to check if the object exists or not. === Javascript does not know how to sum two numbers === Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers. As a result: <pre> var i=1; i += notif.args.increment; // With notif.args.increment='1' alert( i ); // i=11 instead of 2 !! Javascript concatenate 2 strings ! </pre> To solve this, to hesitate to use the "toint" function: <pre> var i=1; i += toint( notif.args.increment ); // With notif.args.increment='1' alert( i ); // i=2 :) </pre> === Javascript: do not use substr with negative numbers === To get the last characters of a string, use "slice" instead of "substr" which has a bug on IE: <pre> var three_last_characters = string.substr( -3 ); // Wrong var three_last_characters = string.slice( -3 ); // Correct </pre> cf42daf00fb7be404a4a30ec9f179847d5a069cc Stock 0 97 633 559 2013-02-02T08:55:38Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. 16282ee586f0e8c2046670ca09ab3d2e371cf099 643 633 2013-02-12T12:11:45Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> 47c1cb6415f093e75bceb1a338885062667c2054 Game interface stylesheet: yourgamename.css 0 96 634 520 2013-02-04T14:48:42Z Sourisdudesert 1 wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('../../img/emptygame/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('../../img/emptygame/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. Important: ALL the CSS directives for your game must be included in this CSS file. You can't create additional CSS files and import them. 9820cd9a56bfee0746b394fc31f1382787b601a5 Gamehelpcantstop 0 35 635 170 2013-02-07T04:14:07Z Karl 2334 wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' Roll 4 standard dice and then choose the sums of any 2 dice to advance your position on the board. You are allowed to use 3 temporary markers to track your progress in columns marked 2 through 12. These markers are commonly white or black. You can continue to roll the dice hoping to roll one of the previous combinations in the three columns selected this turn. When you think you've pushed your luck far enough you say "STOP" then pass the dice to the next player and replace the white markers with your own. If you roll the dice and find no useful combination you fail and your progress is lost. The more common numbers on 2d6 (6,7 and 8) have more steps in their paths while the less common combinations (2 and 12) have fewer. '''The object of the game is''' to reach and cover 3 peaks of these columns! 欲罷不能 如何進行遊戲 首先擲出四顆骰子,之後把四顆骰子兩兩分組配成可以搭配的任意數字(2~12),之後選擇其中一種配對執行。 執行時你可以擁有三個暫時的標記物可以作為前進的工具(此回合超過第三個就不能選該組合中的該項數值,但是其中一項可以前進即可選擇,另外一總組合則視同放棄) 重複以上動作直到你覺得這回合你前進已經夠了或是擲出你沒有辦法前進的骰子點數組合則該回合結束 若是自己喊出「夠了」則在現有的暫時標記物的位置換成自己的標記物輪下一人進行 若是因為擲出不能前進的骰子組合則是本回合前進的格數全部作廢依然換下一人進行回合 遊戲中使用的是兩個六面骰,依照組合的出現機率不同,在不同組合上要攻頂的需求步數也不同(例如7就需要最多格,因為他的出現率最高,相對的比較難出現的2.12就只需要短短的三格) 遊戲獲勝條件是其中有一人佔領了三條路線後則遊戲結束,由佔領三條路線的人獲勝 eb25daa5eaeda4fa4f83b74e41bcddcae0491261 Deck 0 101 636 2013-02-11T16:56:57Z Sourisdudesert 1 Created page with ""Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following fea..." wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... 99108eb430d4ddf064ecc1abd63107dd0fa58251 637 636 2013-02-11T17:05:25Z Sourisdudesert 1 wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck general principles == For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). 3fe6de7e682508ff987b79aa27af2393f512b96d 638 637 2013-02-11T17:23:59Z Sourisdudesert 1 /* Deck general principles */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. * '''type''' and '''type_arg''': These two values defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can start using our "Deck" object. In general, the first 5b42f3001a9bbf08e8e856a5d573d6ffd80ed60b 639 638 2013-02-11T17:35:46Z Sourisdudesert 1 /* Deck overview */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can start using our "Deck" object. In general, the first dbeb5611d5db3a1ebe6fe5ed93f5151f1197b857 640 639 2013-02-11T17:39:39Z Sourisdudesert 1 /* Create a new Deck component */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). bf27635292e5f1fa021037833d79d2eb48e3c25a 641 640 2013-02-11T17:46:47Z Sourisdudesert 1 /* Create a new Deck component */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> d01c941c833ffd3e1318e0ad694112c6c46f2afa 650 641 2013-02-13T17:02:33Z Sourisdudesert 1 wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> == Deck component reference == === Initializing Deck component === '''init( $table_name )''' Initialize the Deck component. Argument: * table_name: name of the DB table used by this Deck component. Must be called before any other Deck method. Usually, init is called in your game constructor. Example with Hearts: <pre> function Hearts( ) { (...) $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); } </pre> '''createCards( $cards, $location='deck', $location_arg=null )''' Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game. "cards" describe all cards that need to be created. "cards" is an array with the following format: <pre> // Create 1 card of type "1" with type_arg=99, // and 4 cards of type "2" with type_arg=12, // and 2 cards of type "3" with type_arg=33 $cards = array( array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ), array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ), array( 'type' => 3, 'type_arg' => 33, nbr' => 2 ) ... ); </pre> Note: During the "createCards" process, Deck generate unique IDs for all card items. Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one. If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location. === Card standard format === When Deck component methods are returning one or several cards, the following format is used: <pre> array( 'id' => .., // the card ID 'type' => .., // the card type 'type_arg' => .., // the card type argument 'location' => .., // the card location 'location_arg' => .. // the card location argument ); <pre> === Picking cards === '''pickCard( $location, $player_id )''' Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player. Return the card picked or "null" if there are no more card in given location. This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCards( $nbr, $location, $player_id )''' Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player. Return an array with the cards picked, or "null" if there are no more card in given location. Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location. This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked. '''pickCardForLocation( $from_location, $to_location, $location_arg=0 )''' This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location. _ from_location is the "pile" style location from where you are picking a card. _ to_location is the location where you will place the card picked. _ if "location_arg" is specified, the card picked will be set with this "location_arg". This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )''' This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location. _ from_location is the "pile" style location from where you are picking some cards. _ to_location is the location where you will place the cards picked. _ if "location_arg" is specified, the cards picked will be set with this "location_arg". _ if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call. This method supports auto-reshuffle (see "auto-reshuffle" below). === Moving cards === '''moveCard( $card_id, $location, $location_arg=0 )''' Move the specific card to given location. _ card_id: ID of the card to move. _ location: location where to move the card. _ location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0. '''moveCards( $cards, $location, $location_arg )''' Move the specific cards to given location. _ cards: an array of IDs of cards to move. _ location: location where to move the cards. _ location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0. '''insertCard( $card_id, $location, $location_arg )''' Move a card to a specific "pile" location where card are ordered. If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location. (note: insertCardOnExtremePosition method below is more useful in most of the case) '''insertCardOnExtremePosition( $card_id, $location, $bOnTop )''' Move a card on top or at bottom of given "pile" type location. '''moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )''' Move all cards in specified "from" location to given location. _ from_location: where to take the cards _ to_location: where to put the cards _ from_location (optional): if specified, only cards with given "location_arg" are moved. _ to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero. Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below. '''moveAllCardsInLocationKeepOrder( $from_location, $to_location )''' Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards. '''playCard( $card_id )''' Move specified card at the top of the "discard" location. Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true ) === Get cards informations === '''getCard( $card_id )''' Get specific card information. Return null if this card is not found. '''getCards( $cards_array )''' Get specific cards information. cards_array is an array of cards ID. If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception. '''getCardsInLocation( $location, $location_arg = null, $order_by = null )''' Get all cards in specific location, as an array. Return an empty array if the location is empty. _ location (string): the location where to get the cards. _ location_arg (optional): if specified, return only cards with the specified "location_arg". _ order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type". '''countCardInLocation( $location, $location_arg=null )''' Return the number of cards in specified location. _ location (string): the location where to count the cards. _ location_arg (optional): if specified, count only cards with the specified "location_arg". '''countCardsInLocations()''' Return the number of cards in each location of the game. The method returns an associative array with the format "location" => "number of cards". Example: <pre> array( 'deck' => 12, 'hand' => 21, 'discard' => 54, 'ontable' => 3 ); </pre> '''countCardsByLocationArgs( $location )''' Return the number of cards in each "location_arg" for the given location. The method returns an associative array with the format "location_arg" => "number of cards". Example: count the number of cards in each player's hand: <pre> countCardsByLocationArgs( 'hand' ); // Result: array( 122345 => 5, // player 122345 has 5 cards in hand 123456 => 4 // and player 123456 has 4 cards in hand ); </pre> '''getPlayerHand( $player_id )''' Get all cards in given player hand. Note: This is an alias for: getCardsInLocation( "hand", $player_id ) '''getCardOnTop( $location )''' Get the card on top of the given ("pile" style) location, or null if the location is empty. Note that the card pile won't be "auto-reshuffled" if there is no more card available. '''getCardsOnTop( $nbr, $location )''' Get the "$nbr" cards on top of the given ("pile" style) location. The method return an array with at most "$nbr" elements (or a void array if there is no card in this location). Note that the card pile won't be "auto-reshuffled" if there is not enough cards available. '''getExtremePosition( $bGetMax ,$location )''' (rarely used) Get the position of cards at the top of the given location / at the bottom of the given location. Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location). If bGetMax=true, return the location of the top card of the pile. If bGetMax=false, return the location of the bottom card of the pile. '''getCardsOfType( $type, $type_arg=null )''' Get all cards of a specific type (rarely used). Return an array of cards, or an empty array if there is no cards of the specified type. _ type: the type of cards _ type_arg: if specified, return only cards with the specified "type_arg". === Shuffling === '''shuffle( $location )''' Shuffle all cards in specific location. Shuffle only works on locations where cards are on a "pile" (ex: "deck"). Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile. 97f690a53ba2e3134edbadeca0c00a07ceff10e2 651 650 2013-02-13T17:03:05Z Sourisdudesert 1 /* Card standard format */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> == Deck component reference == === Initializing Deck component === '''init( $table_name )''' Initialize the Deck component. Argument: * table_name: name of the DB table used by this Deck component. Must be called before any other Deck method. Usually, init is called in your game constructor. Example with Hearts: <pre> function Hearts( ) { (...) $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); } </pre> '''createCards( $cards, $location='deck', $location_arg=null )''' Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game. "cards" describe all cards that need to be created. "cards" is an array with the following format: <pre> // Create 1 card of type "1" with type_arg=99, // and 4 cards of type "2" with type_arg=12, // and 2 cards of type "3" with type_arg=33 $cards = array( array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ), array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ), array( 'type' => 3, 'type_arg' => 33, nbr' => 2 ) ... ); </pre> Note: During the "createCards" process, Deck generate unique IDs for all card items. Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one. If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location. === Card standard format === When Deck component methods are returning one or several cards, the following format is used: <pre> array( 'id' => .., // the card ID 'type' => .., // the card type 'type_arg' => .., // the card type argument 'location' => .., // the card location 'location_arg' => .. // the card location argument ); </pre> === Picking cards === '''pickCard( $location, $player_id )''' Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player. Return the card picked or "null" if there are no more card in given location. This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCards( $nbr, $location, $player_id )''' Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player. Return an array with the cards picked, or "null" if there are no more card in given location. Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location. This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked. '''pickCardForLocation( $from_location, $to_location, $location_arg=0 )''' This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location. _ from_location is the "pile" style location from where you are picking a card. _ to_location is the location where you will place the card picked. _ if "location_arg" is specified, the card picked will be set with this "location_arg". This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )''' This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location. _ from_location is the "pile" style location from where you are picking some cards. _ to_location is the location where you will place the cards picked. _ if "location_arg" is specified, the cards picked will be set with this "location_arg". _ if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call. This method supports auto-reshuffle (see "auto-reshuffle" below). === Moving cards === '''moveCard( $card_id, $location, $location_arg=0 )''' Move the specific card to given location. _ card_id: ID of the card to move. _ location: location where to move the card. _ location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0. '''moveCards( $cards, $location, $location_arg )''' Move the specific cards to given location. _ cards: an array of IDs of cards to move. _ location: location where to move the cards. _ location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0. '''insertCard( $card_id, $location, $location_arg )''' Move a card to a specific "pile" location where card are ordered. If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location. (note: insertCardOnExtremePosition method below is more useful in most of the case) '''insertCardOnExtremePosition( $card_id, $location, $bOnTop )''' Move a card on top or at bottom of given "pile" type location. '''moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )''' Move all cards in specified "from" location to given location. _ from_location: where to take the cards _ to_location: where to put the cards _ from_location (optional): if specified, only cards with given "location_arg" are moved. _ to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero. Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below. '''moveAllCardsInLocationKeepOrder( $from_location, $to_location )''' Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards. '''playCard( $card_id )''' Move specified card at the top of the "discard" location. Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true ) === Get cards informations === '''getCard( $card_id )''' Get specific card information. Return null if this card is not found. '''getCards( $cards_array )''' Get specific cards information. cards_array is an array of cards ID. If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception. '''getCardsInLocation( $location, $location_arg = null, $order_by = null )''' Get all cards in specific location, as an array. Return an empty array if the location is empty. _ location (string): the location where to get the cards. _ location_arg (optional): if specified, return only cards with the specified "location_arg". _ order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type". '''countCardInLocation( $location, $location_arg=null )''' Return the number of cards in specified location. _ location (string): the location where to count the cards. _ location_arg (optional): if specified, count only cards with the specified "location_arg". '''countCardsInLocations()''' Return the number of cards in each location of the game. The method returns an associative array with the format "location" => "number of cards". Example: <pre> array( 'deck' => 12, 'hand' => 21, 'discard' => 54, 'ontable' => 3 ); </pre> '''countCardsByLocationArgs( $location )''' Return the number of cards in each "location_arg" for the given location. The method returns an associative array with the format "location_arg" => "number of cards". Example: count the number of cards in each player's hand: <pre> countCardsByLocationArgs( 'hand' ); // Result: array( 122345 => 5, // player 122345 has 5 cards in hand 123456 => 4 // and player 123456 has 4 cards in hand ); </pre> '''getPlayerHand( $player_id )''' Get all cards in given player hand. Note: This is an alias for: getCardsInLocation( "hand", $player_id ) '''getCardOnTop( $location )''' Get the card on top of the given ("pile" style) location, or null if the location is empty. Note that the card pile won't be "auto-reshuffled" if there is no more card available. '''getCardsOnTop( $nbr, $location )''' Get the "$nbr" cards on top of the given ("pile" style) location. The method return an array with at most "$nbr" elements (or a void array if there is no card in this location). Note that the card pile won't be "auto-reshuffled" if there is not enough cards available. '''getExtremePosition( $bGetMax ,$location )''' (rarely used) Get the position of cards at the top of the given location / at the bottom of the given location. Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location). If bGetMax=true, return the location of the top card of the pile. If bGetMax=false, return the location of the bottom card of the pile. '''getCardsOfType( $type, $type_arg=null )''' Get all cards of a specific type (rarely used). Return an array of cards, or an empty array if there is no cards of the specified type. _ type: the type of cards _ type_arg: if specified, return only cards with the specified "type_arg". === Shuffling === '''shuffle( $location )''' Shuffle all cards in specific location. Shuffle only works on locations where cards are on a "pile" (ex: "deck"). Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile. 30cc7f35ff1f13c94e1d1122b1dafc9e8a0dd77c Main game logic: yourgamename.game.php 0 86 642 590 2013-02-12T11:54:45Z Sourisdudesert 1 /* Notify players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 435c6857bd4d2b9828f2721bd88716e2c3c77355 Gamehelptroyes 0 26 645 54 2013-02-13T16:04:43Z Peach 2372 /* Ziel */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Rules summary == (This rules summary is based on the game help written by [http://boardgamegeek.com/user/pregremlin Andrew Agard] for Board Game Geek, under Creative Commons license. Please see [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] here for details. Thank you Andrew !) ===Initial Placement=== * In clockwise order place a citizen in empty space of one building * Continue in counter clockwise order from last player and so on until all citizens placed ===Game play=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! cac908b237e45cd5a4647769f0b295e42d37c72e 646 645 2013-02-13T16:26:08Z Peach 2372 /* Regel Zusammenfassung */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Initial Placement=== * In clockwise order place a citizen in empty space of one building * Continue in counter clockwise order from last player and so on until all citizens placed ===Spielablauf=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! 4d30bca9173f8e0b9cb90e627c422ac5c3a4acf3 647 646 2013-02-13T16:30:11Z Peach 2372 /* Anfangs Aufstellung */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! 364bbde528d234afd19137233e2b874bc0e74508 648 647 2013-02-13T16:41:16Z Peach 2372 /* Spielablauf */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Aktionskarten aufdecken==== Decke die Aktionskarte für jede Karte entsprechend der aktuellen Runde auf (nur in den ersten 3 Runden) ====Phase 1: Einkommen und Lohn==== Erhalte 10 Deniers und zahle 1 für jeden Bishopric und 2 für jeden Bewohner im Palast oder verliere 2 Siegespunkte ====Phase 2: Aufbau der arbeiterschaft==== Würfle mit gelbem/weißen/roten Würfel für jeden Bewohner im Rathaus/Bishopric/Palast und platziere ihn in deinem Bezirk ====Phase 3: Events==== Decke das oberste rote und weiße oder gelbe Event auf und platziere sie am rechten Ende der Reihe (unbegrenzte Events) Events nehmen Einfluss von links nach rechts * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Aktionen==== Siehe unten. ====Phase 5: Ende der Runde==== Nehme Deniers aus deinem Bezirk Return citizens lying on buildings to personal supplies Return unused dice to general supply Gib die Startspieler-Karte nach links ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! 279f92427ac97dc8d2cf90cb1cb2f33c04c41c42 649 648 2013-02-13T16:43:36Z Peach 2372 /* Aktionen wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Aktionskarten aufdecken==== Decke die Aktionskarte für jede Karte entsprechend der aktuellen Runde auf (nur in den ersten 3 Runden) ====Phase 1: Einkommen und Lohn==== Erhalte 10 Deniers und zahle 1 für jeden Bishopric und 2 für jeden Bewohner im Palast oder verliere 2 Siegespunkte ====Phase 2: Aufbau der arbeiterschaft==== Würfle mit gelbem/weißen/roten Würfel für jeden Bewohner im Rathaus/Bishopric/Palast und platziere ihn in deinem Bezirk ====Phase 3: Events==== Decke das oberste rote und weiße oder gelbe Event auf und platziere sie am rechten Ende der Reihe (unbegrenzte Events) Events nehmen Einfluss von links nach rechts * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Aktionen==== Siehe unten. ====Phase 5: Ende der Runde==== Nehme Deniers aus deinem Bezirk Return citizens lying on buildings to personal supplies Return unused dice to general supply Gib die Startspieler-Karte nach links ===Aktionen=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Aktionskarten aktivieren==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Kampf Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Platzieren einen Bewohner in einem Gebäude==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Benutze Landwirtschaft==== * Erhalte eine Anzahl an Deniers gleich der Summe der Würfel durch 2 (abgerundet) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! a98c831f0b41c9f17c1b958e8162765515b1e1ce Deck 0 101 652 651 2013-02-13T17:03:29Z Sourisdudesert 1 /* Picking cards */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> == Deck component reference == === Initializing Deck component === '''init( $table_name )''' Initialize the Deck component. Argument: * table_name: name of the DB table used by this Deck component. Must be called before any other Deck method. Usually, init is called in your game constructor. Example with Hearts: <pre> function Hearts( ) { (...) $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); } </pre> '''createCards( $cards, $location='deck', $location_arg=null )''' Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game. "cards" describe all cards that need to be created. "cards" is an array with the following format: <pre> // Create 1 card of type "1" with type_arg=99, // and 4 cards of type "2" with type_arg=12, // and 2 cards of type "3" with type_arg=33 $cards = array( array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ), array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ), array( 'type' => 3, 'type_arg' => 33, nbr' => 2 ) ... ); </pre> Note: During the "createCards" process, Deck generate unique IDs for all card items. Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one. If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location. === Card standard format === When Deck component methods are returning one or several cards, the following format is used: <pre> array( 'id' => .., // the card ID 'type' => .., // the card type 'type_arg' => .., // the card type argument 'location' => .., // the card location 'location_arg' => .. // the card location argument ); </pre> === Picking cards === '''pickCard( $location, $player_id )''' Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player. Return the card picked or "null" if there are no more card in given location. This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCards( $nbr, $location, $player_id )''' Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player. Return an array with the cards picked, or "null" if there are no more card in given location. Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location. This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked. '''pickCardForLocation( $from_location, $to_location, $location_arg=0 )''' This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking a card. * to_location is the location where you will place the card picked. * if "location_arg" is specified, the card picked will be set with this "location_arg". This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )''' This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking some cards. * to_location is the location where you will place the cards picked. * if "location_arg" is specified, the cards picked will be set with this "location_arg". * if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call. This method supports auto-reshuffle (see "auto-reshuffle" below). === Moving cards === '''moveCard( $card_id, $location, $location_arg=0 )''' Move the specific card to given location. _ card_id: ID of the card to move. _ location: location where to move the card. _ location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0. '''moveCards( $cards, $location, $location_arg )''' Move the specific cards to given location. _ cards: an array of IDs of cards to move. _ location: location where to move the cards. _ location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0. '''insertCard( $card_id, $location, $location_arg )''' Move a card to a specific "pile" location where card are ordered. If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location. (note: insertCardOnExtremePosition method below is more useful in most of the case) '''insertCardOnExtremePosition( $card_id, $location, $bOnTop )''' Move a card on top or at bottom of given "pile" type location. '''moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )''' Move all cards in specified "from" location to given location. _ from_location: where to take the cards _ to_location: where to put the cards _ from_location (optional): if specified, only cards with given "location_arg" are moved. _ to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero. Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below. '''moveAllCardsInLocationKeepOrder( $from_location, $to_location )''' Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards. '''playCard( $card_id )''' Move specified card at the top of the "discard" location. Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true ) === Get cards informations === '''getCard( $card_id )''' Get specific card information. Return null if this card is not found. '''getCards( $cards_array )''' Get specific cards information. cards_array is an array of cards ID. If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception. '''getCardsInLocation( $location, $location_arg = null, $order_by = null )''' Get all cards in specific location, as an array. Return an empty array if the location is empty. _ location (string): the location where to get the cards. _ location_arg (optional): if specified, return only cards with the specified "location_arg". _ order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type". '''countCardInLocation( $location, $location_arg=null )''' Return the number of cards in specified location. _ location (string): the location where to count the cards. _ location_arg (optional): if specified, count only cards with the specified "location_arg". '''countCardsInLocations()''' Return the number of cards in each location of the game. The method returns an associative array with the format "location" => "number of cards". Example: <pre> array( 'deck' => 12, 'hand' => 21, 'discard' => 54, 'ontable' => 3 ); </pre> '''countCardsByLocationArgs( $location )''' Return the number of cards in each "location_arg" for the given location. The method returns an associative array with the format "location_arg" => "number of cards". Example: count the number of cards in each player's hand: <pre> countCardsByLocationArgs( 'hand' ); // Result: array( 122345 => 5, // player 122345 has 5 cards in hand 123456 => 4 // and player 123456 has 4 cards in hand ); </pre> '''getPlayerHand( $player_id )''' Get all cards in given player hand. Note: This is an alias for: getCardsInLocation( "hand", $player_id ) '''getCardOnTop( $location )''' Get the card on top of the given ("pile" style) location, or null if the location is empty. Note that the card pile won't be "auto-reshuffled" if there is no more card available. '''getCardsOnTop( $nbr, $location )''' Get the "$nbr" cards on top of the given ("pile" style) location. The method return an array with at most "$nbr" elements (or a void array if there is no card in this location). Note that the card pile won't be "auto-reshuffled" if there is not enough cards available. '''getExtremePosition( $bGetMax ,$location )''' (rarely used) Get the position of cards at the top of the given location / at the bottom of the given location. Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location). If bGetMax=true, return the location of the top card of the pile. If bGetMax=false, return the location of the bottom card of the pile. '''getCardsOfType( $type, $type_arg=null )''' Get all cards of a specific type (rarely used). Return an array of cards, or an empty array if there is no cards of the specified type. _ type: the type of cards _ type_arg: if specified, return only cards with the specified "type_arg". === Shuffling === '''shuffle( $location )''' Shuffle all cards in specific location. Shuffle only works on locations where cards are on a "pile" (ex: "deck"). Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile. a290d4ecd4c4c4fee059d59ff4808671f7971637 653 652 2013-02-13T17:03:50Z Sourisdudesert 1 /* Moving cards */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> == Deck component reference == === Initializing Deck component === '''init( $table_name )''' Initialize the Deck component. Argument: * table_name: name of the DB table used by this Deck component. Must be called before any other Deck method. Usually, init is called in your game constructor. Example with Hearts: <pre> function Hearts( ) { (...) $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); } </pre> '''createCards( $cards, $location='deck', $location_arg=null )''' Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game. "cards" describe all cards that need to be created. "cards" is an array with the following format: <pre> // Create 1 card of type "1" with type_arg=99, // and 4 cards of type "2" with type_arg=12, // and 2 cards of type "3" with type_arg=33 $cards = array( array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ), array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ), array( 'type' => 3, 'type_arg' => 33, nbr' => 2 ) ... ); </pre> Note: During the "createCards" process, Deck generate unique IDs for all card items. Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one. If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location. === Card standard format === When Deck component methods are returning one or several cards, the following format is used: <pre> array( 'id' => .., // the card ID 'type' => .., // the card type 'type_arg' => .., // the card type argument 'location' => .., // the card location 'location_arg' => .. // the card location argument ); </pre> === Picking cards === '''pickCard( $location, $player_id )''' Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player. Return the card picked or "null" if there are no more card in given location. This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCards( $nbr, $location, $player_id )''' Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player. Return an array with the cards picked, or "null" if there are no more card in given location. Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location. This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked. '''pickCardForLocation( $from_location, $to_location, $location_arg=0 )''' This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking a card. * to_location is the location where you will place the card picked. * if "location_arg" is specified, the card picked will be set with this "location_arg". This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )''' This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking some cards. * to_location is the location where you will place the cards picked. * if "location_arg" is specified, the cards picked will be set with this "location_arg". * if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call. This method supports auto-reshuffle (see "auto-reshuffle" below). === Moving cards === '''moveCard( $card_id, $location, $location_arg=0 )''' Move the specific card to given location. * card_id: ID of the card to move. * location: location where to move the card. * location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0. '''moveCards( $cards, $location, $location_arg )''' Move the specific cards to given location. * cards: an array of IDs of cards to move. * location: location where to move the cards. * location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0. '''insertCard( $card_id, $location, $location_arg )''' Move a card to a specific "pile" location where card are ordered. If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location. (note: insertCardOnExtremePosition method below is more useful in most of the case) '''insertCardOnExtremePosition( $card_id, $location, $bOnTop )''' Move a card on top or at bottom of given "pile" type location. '''moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )''' Move all cards in specified "from" location to given location. * from_location: where to take the cards * to_location: where to put the cards * from_location (optional): if specified, only cards with given "location_arg" are moved. * to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero. Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below. '''moveAllCardsInLocationKeepOrder( $from_location, $to_location )''' Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards. '''playCard( $card_id )''' Move specified card at the top of the "discard" location. Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true ) === Get cards informations === '''getCard( $card_id )''' Get specific card information. Return null if this card is not found. '''getCards( $cards_array )''' Get specific cards information. cards_array is an array of cards ID. If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception. '''getCardsInLocation( $location, $location_arg = null, $order_by = null )''' Get all cards in specific location, as an array. Return an empty array if the location is empty. _ location (string): the location where to get the cards. _ location_arg (optional): if specified, return only cards with the specified "location_arg". _ order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type". '''countCardInLocation( $location, $location_arg=null )''' Return the number of cards in specified location. _ location (string): the location where to count the cards. _ location_arg (optional): if specified, count only cards with the specified "location_arg". '''countCardsInLocations()''' Return the number of cards in each location of the game. The method returns an associative array with the format "location" => "number of cards". Example: <pre> array( 'deck' => 12, 'hand' => 21, 'discard' => 54, 'ontable' => 3 ); </pre> '''countCardsByLocationArgs( $location )''' Return the number of cards in each "location_arg" for the given location. The method returns an associative array with the format "location_arg" => "number of cards". Example: count the number of cards in each player's hand: <pre> countCardsByLocationArgs( 'hand' ); // Result: array( 122345 => 5, // player 122345 has 5 cards in hand 123456 => 4 // and player 123456 has 4 cards in hand ); </pre> '''getPlayerHand( $player_id )''' Get all cards in given player hand. Note: This is an alias for: getCardsInLocation( "hand", $player_id ) '''getCardOnTop( $location )''' Get the card on top of the given ("pile" style) location, or null if the location is empty. Note that the card pile won't be "auto-reshuffled" if there is no more card available. '''getCardsOnTop( $nbr, $location )''' Get the "$nbr" cards on top of the given ("pile" style) location. The method return an array with at most "$nbr" elements (or a void array if there is no card in this location). Note that the card pile won't be "auto-reshuffled" if there is not enough cards available. '''getExtremePosition( $bGetMax ,$location )''' (rarely used) Get the position of cards at the top of the given location / at the bottom of the given location. Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location). If bGetMax=true, return the location of the top card of the pile. If bGetMax=false, return the location of the bottom card of the pile. '''getCardsOfType( $type, $type_arg=null )''' Get all cards of a specific type (rarely used). Return an array of cards, or an empty array if there is no cards of the specified type. _ type: the type of cards _ type_arg: if specified, return only cards with the specified "type_arg". === Shuffling === '''shuffle( $location )''' Shuffle all cards in specific location. Shuffle only works on locations where cards are on a "pile" (ex: "deck"). Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile. f4a855fc2b2063c8daae2d2cf77bbaf3876e6401 654 653 2013-02-13T17:04:14Z Sourisdudesert 1 /* Get cards informations */ wikitext text/x-wiki "Deck" is one of the most useful component on PHP side. With "Deck", you can manage cards of your game on server side. Using "deck", you will be able to use the following features without writing a single SQL database request: * Place cards in pile, shuffle cards, draw cards one by one or many by many. * "Auto-reshuffle" discard pile into deck when deck is empty. * Move cards between different locations: hands of players, the table, ... == Using Deck: Hearts example == Deck component is massively used in "Hearts" example game - a card game. You can find in "hearts.game.php" that the object "$this->cards" is used many times. == Deck overview == With Deck component, you manage all cards of your game. === The 5 properties of each card === Using Deck component, each card will have 5 properties: * '''id''': This is the unique ID of each card. * '''type''' and '''type_arg''': These two values defines the type of your card (=what sort of card this is?). * '''location''' and '''location_arg''': These two values defines where is the card at now. id, type and type_arg properties are constants during the game. location and location_arg are changing when your cards are moving from places to places on the game area. '''id''' is the unique ID of each card. Two cards can't get the same ID. IDs are generated automatically by the Deck component when you create cards during the Setup phase of your game. '''type''' and '''type_arg''' defines the type of your card. '''type''' is a short string, and '''type_arg''' is an integer. You can use these two values as you want to make sure you will be able to identify the different cards of the game. See usage of "type" and "type_arg" below. Examples of usage of "type" and "type_arg": * In "Hearts", "type" is the color of the card (1 to 4) and "type_arg" is the value of the card (1, 2, ... 10, J, Q, K). * In "Seasons", "type" is the type of the card (ex: 1 is Amulet of Air, 2 is Amulet of Fire, etc...). type_arg is not used. * In "Takenoko", a Deck component is used for objective cards. "type" is the kind of objective (irrigation/panda/plot) and "type_arg" is the ID of the specific objective to realize (ex: "a green bamboo x4"). Note that a second Deck component is used in Takenoko to manage the "garden plot" pile. '''location''' and '''location_arg''' defines where is the card at now. '''location''' is a short string, and '''location_arg''' is an integer. You can use 'location' and 'location_arg' as you want, to move your card on the game area. Although, there are 3 special 'location' that Deck manage specifically. You can choose to use - or not to use - these locations depending of your needs: * 'deck': in 'deck' location, cards are placed face down in a pile and are drawn during the game. 'location_arg' is used to specify where the card is in the deck pile (the card with the biggest location_arg is the next to be drawn). * 'hand': in 'hand' location, cards are in the hand of a player. 'location_arg' is set to the ID of this player. * 'discard': in 'discard' location, cards are discarded, and are ready to be shuffled into the deck if needed (see "autoreshuffle"). Tips: using Deck component, you are going to use generic properties ("location", "type_arg",...) for specific purposes of your game. Thus, during the design step before realizing your game, take 2 minutes to write down what is the meaning of each of this generic properties in the context of your game. === Create a new Deck component === For each Deck component in your game, you need to create a dedicated table in database. This table has a standard format. In practical, if you want to have a Deck component named "card", you just have to copy/paste the following in your "dbmodel.sql": <pre> CREATE TABLE IF NOT EXISTS `card` ( `card_id` int(10) unsigned NOT NULL AUTO_INCREMENT, `card_type` varchar(16) NOT NULL, `card_type_arg` int(11) NOT NULL, `card_location` varchar(16) NOT NULL, `card_location_arg` int(11) NOT NULL, PRIMARY KEY (`card_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Once you did this (and restart your game), you can declare your Deck component in your PHP code in your class constructor. For Hearts for example, I added to "Hearts()" method: <pre> $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); </pre> Note that we specify "card" here, the name of our previously created table. It means you can create several "Deck" with several tables. Most of the time this is unuseful: a Deck component should manage all objects of the same kind (ex: all cards of the game). Afterwards, we can initialize your "Deck" by creating all the cards of the game. Generally, this is done only once during the game, during the "setupNewGame" method. "Deck" component provides you a fast way to initialize all your cards at once: createCards. Here is how it is used for "Hearts": <pre> // Create cards $cards = array(); foreach( $this->colors as $color_id => $color ) // spade, heart, diamond, club { for( $value=2; $value<=14; $value++ ) // 2, 3, 4, ... K, A { $cards[] = array( 'type' => $color_id, 'type_arg' => $value, 'nbr' => 1); } } $this->cards->createCards( $cards, 'deck' ); </pre> As you can see, "createCards" takes a description of all cards of the game. For each type of card, you have to specify its "type", "type_arg" and the number of card to create. "createCards" create all cards and place them into the "deck" location (as specified in the second argument). Now, you are ready to use "Deck"! === Simple examples using Deck === (Most examples are from "Hearts" game) <pre> // In "getAllDatas', we need to send to the current player all the cards he has in hand: $result['hand'] = $this->cards->getCardsInLocation( 'hand', $player_id ); </pre> <pre> // At some time we want to check if all the cards (52) are in player's hands: if( $this->cards->countCardInLocation( 'hand' ) == 52 ) // do something </pre> <pre> // When a player plays a card in front of him on the table: $this->cards->moveCard( $card_id, 'cardsontable', $player_id ); // Note the use of the custom location 'cardsontable' here to keep track of cards on the table. </pre> <pre> // This is a new hand: let's gather all cards from everywhere in the deck: $this->cards->moveAllCardsInLocation( null, "deck" ); // And then shuffle the deck $this->cards->shuffle( 'deck' ); // And then deal 13 cards to each player // Deal 13 cards to each players // Create deck, shuffle it and give 13 initial cards $players = self::loadPlayersBasicInfos(); foreach( $players as $player_id => $player ) { $cards = $this->cards->pickCards( 13, 'deck', $player_id ); // Notify player about his cards self::notifyPlayer( $player_id, 'newHand', '', array( 'cards' => $cards ) ); } // Note the use of "notifyPlayer" instead of "notifyAllPlayers": new cards is a private information ;) </pre> == Deck component reference == === Initializing Deck component === '''init( $table_name )''' Initialize the Deck component. Argument: * table_name: name of the DB table used by this Deck component. Must be called before any other Deck method. Usually, init is called in your game constructor. Example with Hearts: <pre> function Hearts( ) { (...) $this->cards = self::getNew( "module.common.deck" ); $this->cards->init( "card" ); } </pre> '''createCards( $cards, $location='deck', $location_arg=null )''' Create card items in your deck component. Usually, all card items are created once, during the setup phase of the game. "cards" describe all cards that need to be created. "cards" is an array with the following format: <pre> // Create 1 card of type "1" with type_arg=99, // and 4 cards of type "2" with type_arg=12, // and 2 cards of type "3" with type_arg=33 $cards = array( array( 'type' => 1, 'type_arg' => 99, nbr' => 1 ), array( 'type' => 2, 'type_arg' => 12, nbr' => 4 ), array( 'type' => 3, 'type_arg' => 33, nbr' => 2 ) ... ); </pre> Note: During the "createCards" process, Deck generate unique IDs for all card items. Note: createCards is optimized to create a lot of cards at once. Do not use it to create cards one by one. If "location" and "location_arg" arguments are not set, newly created cards are placed in the "deck" location. If "location" (and optionally location_arg) is specified, cards are created for this specific location. === Card standard format === When Deck component methods are returning one or several cards, the following format is used: <pre> array( 'id' => .., // the card ID 'type' => .., // the card type 'type_arg' => .., // the card type argument 'location' => .., // the card location 'location_arg' => .. // the card location argument ); </pre> === Picking cards === '''pickCard( $location, $player_id )''' Pick a card from a "pile" location (ex: "deck") and place it in the "hand" of specified player. Return the card picked or "null" if there are no more card in given location. This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCards( $nbr, $location, $player_id )''' Pick "$nbr" cards from a "pile" location (ex: "deck") and place them in the "hand" of specified player. Return an array with the cards picked, or "null" if there are no more card in given location. Note that the number of cards picked can be less than "$nbr" in case there are not enough cards in the pile location. This method supports auto-reshuffle (see "auto-reshuffle" below). In case there are not enough cards in the pile, all remaining cards are picked first, then the auto-reshuffle is triggered, then the other cards are picked. '''pickCardForLocation( $from_location, $to_location, $location_arg=0 )''' This method is similar to 'pickCard', except that you can pick a card for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking a card. * to_location is the location where you will place the card picked. * if "location_arg" is specified, the card picked will be set with this "location_arg". This method supports auto-reshuffle (see "auto-reshuffle" below). '''pickCardsForLocation( $nbr, $from_location, $to_location, $location_arg=0, $no_deck_reform=false )''' This method is similar to 'pickCards', except that you can pick cards for any sort of location and not only the "hand" location. * from_location is the "pile" style location from where you are picking some cards. * to_location is the location where you will place the cards picked. * if "location_arg" is specified, the cards picked will be set with this "location_arg". * if "no_deck_reform" is set to "true", the auto-reshuffle feature is disabled during this method call. This method supports auto-reshuffle (see "auto-reshuffle" below). === Moving cards === '''moveCard( $card_id, $location, $location_arg=0 )''' Move the specific card to given location. * card_id: ID of the card to move. * location: location where to move the card. * location_arg: if specified, location_arg where to move the card. If not specified "location_arg" will be set to 0. '''moveCards( $cards, $location, $location_arg )''' Move the specific cards to given location. * cards: an array of IDs of cards to move. * location: location where to move the cards. * location_arg: if specified, location_arg where to move the cards. If not specified "location_arg" will be set to 0. '''insertCard( $card_id, $location, $location_arg )''' Move a card to a specific "pile" location where card are ordered. If location_arg place is already taken, increment all cards after location_arg in order to insert new card at this precise location. (note: insertCardOnExtremePosition method below is more useful in most of the case) '''insertCardOnExtremePosition( $card_id, $location, $bOnTop )''' Move a card on top or at bottom of given "pile" type location. '''moveAllCardsInLocation( $from_location, $to_location, $from_location_arg=null, $to_location_arg=0 )''' Move all cards in specified "from" location to given location. * from_location: where to take the cards * to_location: where to put the cards * from_location (optional): if specified, only cards with given "location_arg" are moved. * to_location (optional): if specified, cards moved "location_arg" is set to given value. Otherwise location_arg is set to zero. Note: if you want to keep "location_arg" untouched, you should use "moveAllCardsInLocationKeepOrder" below. '''moveAllCardsInLocationKeepOrder( $from_location, $to_location )''' Move all cards in specified "from" location to given "to" location. This method does not modify the "location_arg" of cards. '''playCard( $card_id )''' Move specified card at the top of the "discard" location. Note: this is an alias for: insertCardOnExtremePosition( $card_id, "discard", true ) === Get cards informations === '''getCard( $card_id )''' Get specific card information. Return null if this card is not found. '''getCards( $cards_array )''' Get specific cards information. cards_array is an array of cards ID. If some cards are not found or if some cards IDs are specified multiple times, the method throws an (unexpected) Exception. '''getCardsInLocation( $location, $location_arg = null, $order_by = null )''' Get all cards in specific location, as an array. Return an empty array if the location is empty. * location (string): the location where to get the cards. * location_arg (optional): if specified, return only cards with the specified "location_arg". * order_by (optional): if specified, returned cards are ordered by the given database field. Example: "card_id" or "card_type". '''countCardInLocation( $location, $location_arg=null )''' Return the number of cards in specified location. * location (string): the location where to count the cards. * location_arg (optional): if specified, count only cards with the specified "location_arg". '''countCardsInLocations()''' Return the number of cards in each location of the game. The method returns an associative array with the format "location" => "number of cards". Example: <pre> array( 'deck' => 12, 'hand' => 21, 'discard' => 54, 'ontable' => 3 ); </pre> '''countCardsByLocationArgs( $location )''' Return the number of cards in each "location_arg" for the given location. The method returns an associative array with the format "location_arg" => "number of cards". Example: count the number of cards in each player's hand: <pre> countCardsByLocationArgs( 'hand' ); // Result: array( 122345 => 5, // player 122345 has 5 cards in hand 123456 => 4 // and player 123456 has 4 cards in hand ); </pre> '''getPlayerHand( $player_id )''' Get all cards in given player hand. Note: This is an alias for: getCardsInLocation( "hand", $player_id ) '''getCardOnTop( $location )''' Get the card on top of the given ("pile" style) location, or null if the location is empty. Note that the card pile won't be "auto-reshuffled" if there is no more card available. '''getCardsOnTop( $nbr, $location )''' Get the "$nbr" cards on top of the given ("pile" style) location. The method return an array with at most "$nbr" elements (or a void array if there is no card in this location). Note that the card pile won't be "auto-reshuffled" if there is not enough cards available. '''getExtremePosition( $bGetMax ,$location )''' (rarely used) Get the position of cards at the top of the given location / at the bottom of the given location. Of course this method works only on location in "pile" where you are using "location_arg" to specify the position of each card (example: "deck" location). If bGetMax=true, return the location of the top card of the pile. If bGetMax=false, return the location of the bottom card of the pile. '''getCardsOfType( $type, $type_arg=null )''' Get all cards of a specific type (rarely used). Return an array of cards, or an empty array if there is no cards of the specified type. * type: the type of cards * type_arg: if specified, return only cards with the specified "type_arg". === Shuffling === '''shuffle( $location )''' Shuffle all cards in specific location. Shuffle only works on locations where cards are on a "pile" (ex: "deck"). Please note that all "location_arg" will be reseted to reflect the new order of the cards in the pile. 888d00b0a38148f59e241b98b3bb921eba1403f2 Game interface logic: yourgamename.js 0 88 655 644 2013-02-13T17:07:00Z Sourisdudesert 1 wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> === Player's panel disabling/enabling '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 317bdc295b668f5ee01046885997fe77b6f0c0de 656 655 2013-02-13T17:07:21Z Sourisdudesert 1 /* Update players score */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 6c6bae4526ba6f719fe08686230ab499d1dbbec7 657 656 2013-02-13T17:07:28Z Sourisdudesert 1 /* Player's panel disabling/enabling */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Player's panel disabling/enabling == '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 2af44a5cdab3cfff195bc95c9cf3b64104568a06 673 657 2013-03-06T14:47:17Z Sourisdudesert 1 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Player's panel disabling/enabling == '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> bd972f271ef42252114b8f4d86ef99ebfc8ad83f 674 673 2013-03-06T14:52:26Z Sourisdudesert 1 /* Player's panel disabling/enabling */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Player's panel disabling/enabling == '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> bbdd8317ec1cbd390a37b6e0dbe5659007b947ff 676 674 2013-03-06T15:02:01Z Sourisdudesert 1 /* Player's panel disabling/enabling */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> bc879e9abee75c711f10c9937ff891393783156f 682 676 2013-03-06T15:18:06Z Sourisdudesert 1 /* General tips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( node, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( $('cardcount'), _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( node, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> e1ea57068eed4a0c9bc22b8972fd0e776b21faee Gamehelpcoloretto 0 33 658 192 2013-02-14T22:53:02Z Peach 2372 wikitext text/x-wiki Während man im regulären Spiel entscheiden muss, welche Stapel positiv/negativ gewertet werden und wo man die Joker platziert, errechnet diese Version automatisch den höchst möglichen Score. 7f464fbdfa930182f9877ed6fef567378c23823a Faq 0 3 659 100 2013-02-15T07:41:51Z Larrygates 2403 /* What are the games available on BGA? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [[Board Game Arena club]] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 437cf4ae7997f52afc3d4a93addca089139ad5f6 Practical debugging 0 100 660 632 2013-02-15T15:09:18Z Een 3 /* Javascript does not know how to sum two numbers */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. === When I do a move, I got "Move recorded, waiting for update ..." forever === "Move recorded" means that your ajaxcall request has been sent to the server and returned normally. "Waiting for update" means that your client interface is waiting for some notifications from the server that correspond to the move we just did. If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code. === Some player action is triggered randomly when I click somewhere on the game area === You probably used "dojo.connect" on a null object. In this case, dojo.connect associate the event (ex: "onclick") to the whole game area. Most of the time it happens in this situation, when my_object element does not exists: <pre> dojo.connect( $("my_object"), "onclick", this, function() { ... } </pre> To determine if this is the case, place "alert( $("my_object") )" before the dojo.connect to check if the object exists or not. === Javascript does not know how to sum two numbers === Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers. As a result: <pre> var i=1; i += notif.args.increment; // With notif.args.increment='1' alert( i ); // i=11 instead of 2 !! Javascript concatenate 2 strings ! </pre> To solve this, you should use the "toint" function: <pre> var i=1; i += toint( notif.args.increment ); // With notif.args.increment='1' alert( i ); // i=2 :) </pre> === Javascript: do not use substr with negative numbers === To get the last characters of a string, use "slice" instead of "substr" which has a bug on IE: <pre> var three_last_characters = string.substr( -3 ); // Wrong var three_last_characters = string.slice( -3 ); // Correct </pre> d86a3fa2d7ac9e139610b3420fc0d619ea9fc38a Gamehelptroyes 0 26 661 649 2013-02-15T15:14:24Z Pablo74 19 /* Phase 1: Einkommen und Lohn */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Aktionskarten aufdecken==== Decke die Aktionskarte für jede Karte entsprechend der aktuellen Runde auf (nur in den ersten 3 Runden) ====Phase 1: Einkommen und Lohn==== Erhalte 10 Deniers und zahle 1 Denier für jeden deiner Bewohner im Bischofssitz und 2 Deniers für jeden deiner Bewohner im Palast oder verliere 2 Siegespunkte ====Phase 2: Aufbau der arbeiterschaft==== Würfle mit gelbem/weißen/roten Würfel für jeden Bewohner im Rathaus/Bishopric/Palast und platziere ihn in deinem Bezirk ====Phase 3: Events==== Decke das oberste rote und weiße oder gelbe Event auf und platziere sie am rechten Ende der Reihe (unbegrenzte Events) Events nehmen Einfluss von links nach rechts * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Aktionen==== Siehe unten. ====Phase 5: Ende der Runde==== Nehme Deniers aus deinem Bezirk Return citizens lying on buildings to personal supplies Return unused dice to general supply Gib die Startspieler-Karte nach links ===Aktionen=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Aktionskarten aktivieren==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Kampf Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Platzieren einen Bewohner in einem Gebäude==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Benutze Landwirtschaft==== * Erhalte eine Anzahl an Deniers gleich der Summe der Würfel durch 2 (abgerundet) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! 87a4c2e79c8cb54682718deadfb347051f4a4664 662 661 2013-02-15T15:15:46Z Pablo74 19 /* Phase 5: Ende der Runde */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Aktionskarten aufdecken==== Decke die Aktionskarte für jede Karte entsprechend der aktuellen Runde auf (nur in den ersten 3 Runden) ====Phase 1: Einkommen und Lohn==== Erhalte 10 Deniers und zahle 1 Denier für jeden deiner Bewohner im Bischofssitz und 2 Deniers für jeden deiner Bewohner im Palast oder verliere 2 Siegespunkte ====Phase 2: Aufbau der arbeiterschaft==== Würfle mit gelbem/weißen/roten Würfel für jeden Bewohner im Rathaus/Bishopric/Palast und platziere ihn in deinem Bezirk ====Phase 3: Events==== Decke das oberste rote und weiße oder gelbe Event auf und platziere sie am rechten Ende der Reihe (unbegrenzte Events) Events nehmen Einfluss von links nach rechts * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Aktionen==== Siehe unten. ====Phase 5: Ende der Runde==== Nimm Deniers aus deinem Bezirk Nimm deine liegenden Bürger aus den Gebäuden in deinen persönlichen Vorrat Unbenutze Würfel gehen zurück in den allgemeinen Vorrat Gib die Startspieler-Karte nach links ===Aktionen=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Aktionskarten aktivieren==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Kampf Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Platzieren einen Bewohner in einem Gebäude==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Benutze Landwirtschaft==== * Erhalte eine Anzahl an Deniers gleich der Summe der Würfel durch 2 (abgerundet) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! e8990947207bb3915058a4f1087c7046155f59bd 665 662 2013-02-21T15:08:59Z Peach 2372 /* Regel Zusammenfassung */ wikitext text/x-wiki == Ziel == Sei der Spieler mit den meisten Siegespunkten am Ende des Spiels. == Regel Zusammenfassung == (Diese Regel Zusammenfassung basiert auf der Spielhilfe von [http://boardgamegeek.com/user/pregremlin Andrew Agard] für Board Game Geek, unter Creative Commons Lizenz. Bitte schauen Sie [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] hier für Details. Vielen Dank Andrew !) ===Anfangs Aufstellung=== * Im Uhrzeigersinn werden werden die Bewohner in freie Felder der Gebäude gesetzt * Fahre gegen den Uhrzeigersinn fort, ausgehend vom letzten Spieler, bis alle Bewohner verteilt sind ===Spielablauf=== ====Phase 0: Aktionskarten aufdecken==== Decke die Aktionskarte für jede Karte entsprechend der aktuellen Runde auf (nur in den ersten 3 Runden) ====Phase 1: Einkommen und Lohn==== Erhalte 10 Deniers und zahle 1 Denier für jeden deiner Bewohner im Bischofssitz und 2 Deniers für jeden deiner Bewohner im Palast oder verliere 2 Siegespunkte ====Phase 2: Aufbau der Arbeiterschaft==== Würfle mit gelbem/weißen/roten Würfel für jeden Bewohner im Rathaus/Bishopric/Palast und platziere ihn in deinem Bezirk ====Phase 3: Events==== Decke das oberste rote und weiße oder gelbe Event auf und platziere sie am rechten Ende der Reihe (unbegrenzte Events) Events nehmen Einfluss von links nach rechts * Militär: Der Startspieler nimmt für jedes Würfelsymbol einen schwarzen Würfel * Andere Events: siehe Anhang (Bekämpfung nicht möglich: bekämpfe so viel wie möglich und verliere 2 Siegpunkte) Nach den Events, Rolle den schwarzen Würfel und kontere: * Startspieler muss den höchsten Wert mit einem oder mehreren Würfeln aus seinem Bezirk kontern. Gesamtwert muss >= Würfel sein. Mehrere Würfel gleichzeitig kontern ist möglich * Erhalte 1 Einflusspunkt für jeden gekonterten Würfel (kontern nicht möglich: entferne Würfel und verliere 2 Siegpunkte) * Im Uhrzeigersinn müssen die Spieler den höchsten übrig geblieben kontern bis alle gekontert sind * Benutze Würfel jeder Farbe. Rote Würfel werden verdoppelt. Würfel können nicht gekauft und Aktionen nicht ausgeübt werden. ====Phase 4: Aktionen==== Siehe unten. ====Phase 5: Ende der Runde==== Nimm Deniers aus deinem Bezirk Nimm deine liegenden Bürger aus den Gebäuden in deinen persönlichen Vorrat Unbenutze Würfel gehen zurück in den allgemeinen Vorrat Gib die Startspieler-Karte nach links ===Aktionen=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Aktionskarten aktivieren==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Kampf Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Platzieren einen Bewohner in einem Gebäude==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Benutze Landwirtschaft==== * Erhalte eine Anzahl an Deniers gleich der Summe der Würfel durch 2 (abgerundet) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! 10e7539cf69405e6f021f88b33134b76ab2668a6 Gamehelpquoridor 0 102 663 2013-02-19T00:19:11Z Magicalvin 2439 Quoridor Request for Different Size Board wikitext text/x-wiki Hi,is it possible to edit the Quoridor board to different size board? ie 15x15 and different number of fences? ie 20 acbb73649bc695ffaf83329331452d7bab6fff75 664 663 2013-02-20T03:50:12Z Kaminix 2393 Page emptied (non-rule text) wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 666 664 2013-02-24T03:36:21Z Airandfingers 2504 wikitext text/x-wiki Corridor is a 2-player game, the object of which is to move your pawn to the other side of a 9x9 board. e5f5b0f70c54b52e359d958322b786afb0a71567 667 666 2013-02-28T09:23:03Z Magicalvin 2439 wikitext text/x-wiki Corridor is a 2-player game, the object of which is to move your pawn to the other side of a 9x9 board before your opponent. You can either move your man up,down or sideways or put a fence down. Players can jump over an opponent that he is next to or jump diagonally if he is next to a fence. Players cannot jump over fences. Players can put a fence down to block an opponent, but must allow at least one path to get to his winning side. You have only 10 fences. There is a fence counter by your name. Set traps to delay the opponent and create corridors to a winning path. You can concede the game by clicking on the square by your name and clicking "Concede...". You must have completed 50% or more of your game. There is an optional starting "Off the Center Aisle" Variation. Players agree to play this variation. The first player moves sideways and the second player move sideways in the opposite direction. The players keep on moving sideways until they agree to stop. 4adf60484c8ac4fea674576ec269f7e4d43c9364 695 667 2013-03-16T17:38:00Z Sirius136 2706 wikitext text/x-wiki [http://heyjude0929.pixnet.net/blog/post/26923277-%5B%E9%81%8A%E6%88%B2%E4%BB%8B%E7%B4%B9%5D%E6%AD%A5%E6%AD%A5%E7%82%BA%E7%87%9Fquoridor]步步為營(zh) Corridor is a 2-player game, the object of which is to move your pawn to the other side of a 9x9 board before your opponent. You can either move your man up,down or sideways or put a fence down. Players can jump over an opponent that he is next to or jump diagonally if he is next to a fence. Players cannot jump over fences. Players can put a fence down to block an opponent, but must allow at least one path to get to his winning side. You have only 10 fences. There is a fence counter by your name. Set traps to delay the opponent and create corridors to a winning path. You can concede the game by clicking on the square by your name and clicking "Concede...". You must have completed 50% or more of your game. There is an optional starting "Off the Center Aisle" Variation. Players agree to play this variation. The first player moves sideways and the second player move sideways in the opposite direction. The players keep on moving sideways until they agree to stop. 81f6185c44e295d3d224159201b0b4d3a368fc64 Keskustelu:Gamehelphearts 1 103 668 2013-03-02T23:23:01Z Chandler123 2569 Created page with "it seems to me that if you shoot the moon you should have the option to increase your own score or decrease the score of the other players" wikitext text/x-wiki it seems to me that if you shoot the moon you should have the option to increase your own score or decrease the score of the other players 5cd6ca4386ce8f33f76e892c009dd7ccc85c583b 671 668 2013-03-05T04:16:20Z Msome 2487 wikitext text/x-wiki chandler123: it seems to me that if you shoot the moon you should have the option to increase your own score or decrease the score of the other players. msome: That is common practice in the real world, but would make the length of an online game harder to predict, a problem for some players. I'd like to see an in-between solution: shooting the moon decreases the score of other players, unless doing so would end the game with you not in 1st place... in which case it instead increases your score by 26. In any case this discussion should be in the Forum, as I doubt anyone is reaing this talk page :) 7c6db170d1f2268afb4880b703201159b31b1afa Gamehelpseasons 0 43 669 185 2013-03-03T05:24:18Z Thoradian 997 wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each card and the crystal owned. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. Card summon by other card but due to limitation of the star will cause the new card to be discarded, no refund. 5552e6fb806e97da9943bc61794ef1a78d500b88 Gamehelphearts 0 52 670 234 2013-03-05T04:08:41Z Msome 2487 moon /slam etiquette wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your hand, choose three cards to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player across the table, (3rd hand) to the player on your right, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as "shooting the moon", or "slam"), that player scores 0 and all other players score -26. Warning other players about an attempt to shoot the moon is team play, and a breach of Hearts etiquette. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. 30e89bee40751176339d12f280b2f8d9cb48a3ca Gamehelptakenoko 0 104 672 2013-03-06T03:32:42Z Terrycloth 2601 Created page with "The game appears to think that the plot objectives are oriented. Since the rules say nothing about this it might be a bug." wikitext text/x-wiki The game appears to think that the plot objectives are oriented. Since the rules say nothing about this it might be a bug. 9008df23bbb966b1f34a0f89c9c6ade47840bd7a 696 672 2013-03-17T02:30:13Z Jirikki 2655 wikitext text/x-wiki Quick Reference Guide Each turn consists of 2 steps: 1) Determine weather 2) Perform 2 different actions and complete objectives Weather Types (always optional) Sun - take a 3rd action Rain - grow 1 bamboo on any irrigated plot Wind - take two identical actions Storm - move panda to any plot and he eats a bamboo as normal Clouds - take an improvement chip from the bank, if no chips are available, choose 1 of the other 4 types of weather for this turn Improvements can only be placed on plots with no bamboo. There can only be 1 improvement on a plot and once placed cannot be changed. Irrigation channels must be between two plots. When a plot is irrigated for the first time it grows bamboo. Plots can only be placed adjacent to the pond or where it is next to 2 other plots in play. The gardener grows bamboo on his plot and all adjacent irrigated plots of the same color. Game ends when a certain number of objectives have been completed. 2 players - 9 objectives 3 players - 8 objectives 4 players - 7 objectives The first player to complete the required number of objectives triggers the final round and scores 2 bonus points. The other players have a final turn. The highest score wins, the tie breaker is number of points on panda objectives. If the score is still tied then all tied players win. fa07912e1a73702b1075cc1d4d35db8b91949a7d 697 696 2013-03-17T02:33:50Z Jirikki 2655 wikitext text/x-wiki Quick Reference Guide Each turn consists of 2 steps: 1) Determine weather 2) Perform 2 different actions and complete objectives Weather Types (always optional) Sun - take a 3rd action Rain - grow 1 bamboo on any irrigated plot Wind - take two identical actions Storm - move panda to any plot and he eats a bamboo as normal Clouds - take an improvement chip from the bank, if no chips are available, choose 1 of the other 4 types of weather for this turn Improvements can only be placed on plots with no bamboo. There can only be 1 improvement on a plot and once placed cannot be changed. Irrigation channels must be between two plots. When a plot is irrigated for the first time it grows bamboo. Plots can only be placed adjacent to the pond or where it is next to 2 other plots in play. The gardener grows bamboo on his plot and all adjacent irrigated plots of the same color. Game ends when a certain number of objectives have been completed. 2 players - 9 objectives 3 players - 8 objectives 4 players - 7 objectives The first player to complete the required number of objectives triggers the final round and scores 2 bonus points. The other players have a final turn. The highest score wins, the tie breaker is number of points on panda objectives. If the score is still tied then all tied players win. 2b1801a348c4d02de75d3218816726ed3f00ca4a Translations 0 94 675 606 2013-03-06T14:56:14Z Sourisdudesert 1 /* How to not make translators crazy ;) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short strings. * The BGA translation policy is to be flexible on grammar... We prefer to write "player gets 1 coin(s)" than write two versions of the same string for plural and singular - it reduces the number of strings to translate. * Instead of writing nice strings like "With the effect of ZZZ, player XXX gets a new YYY", which is very difficult to translate, write strings like "ZZZ: XXX gets YYY". == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> Note: what is also possible to do is == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php b93a5f713e0a56d8e521c6d6cc54a2520dffbb0f Stock 0 97 677 643 2013-03-06T15:05:36Z Sourisdudesert 1 wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''1st case''' When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''2nd case''' When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''3rd case''' When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly 924af9cf8ebeed02fd579a8fa62baf6ca78c34f6 678 677 2013-03-06T15:06:03Z Sourisdudesert 1 /* Tips when adding/removing items to/from Stock components */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly 46fa0666b198c0c2ba93d6edcb88858e35fb57a7 699 678 2013-03-20T16:59:00Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: _ type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. _ weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). _ image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly a38bf1471a7860fdd10e302c07b6b1770bb18825 700 699 2013-03-20T16:59:47Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> _ image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly c61aed62adbb829cc6421e4d740a4d9eab88e1f5 701 700 2013-03-20T17:00:01Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly b31bae02a5f0ea27a4deff6893d5d96574136897 Main game logic: yourgamename.game.php 0 86 679 642 2013-03-06T15:09:36Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method should be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 53093afae2c0bf7fb0a989679b2b7f3977453591 680 679 2013-03-06T15:09:55Z Sourisdudesert 1 /* Game states and active players */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. 5f3667a959afcc309f1ac96079dcb6fa777af169 686 680 2013-03-06T15:32:43Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. 7406f722579bbd5bcf3e69a11feb4e5ac8df0a76 687 686 2013-03-06T15:35:02Z Sourisdudesert 1 /* Zombie mode */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new feException( "Zombie mode not supported at this game state: ".$statename ); } </pre> 8897bcce501f79e77cb6106fb3652f3879e79803 688 687 2013-03-06T15:35:34Z Sourisdudesert 1 /* Zombie mode */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new feException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. 1bbaf94790e736345d32cf42d40095e027660046 Game interface stylesheet: yourgamename.css 0 96 681 634 2013-03-06T15:17:28Z Sourisdudesert 1 wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('../../img/emptygame/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('../../img/emptygame/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. Important: ALL the CSS directives for your game must be included in this CSS file. You can't create additional CSS files and import them. == spectatorMode == fd49cb098fa0e7edace88c6cc5445536ca0527ec 683 681 2013-03-06T15:20:58Z Sourisdudesert 1 /* spectatorMode */ wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('../../img/emptygame/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('../../img/emptygame/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. Important: ALL the CSS directives for your game must be included in this CSS file. You can't create additional CSS files and import them. == spectatorMode == When a spectator (= a player that is not part of the game) is viewing a game, the BGA framework add the CSS class "spectatorMode" to the wrapping HTML tag of your game. This way, if you want to apply a special style to some elements of your game for spectators, you can do this in your CSS: <pre> .spectatorMode #your_element_id { /* your special style */ } </pre> The most common usage of this is to hide some elements to spectators. For example, to hide "my hand" elements: <pre> .spectatorMode #my_hand { display: none; } </pre> 88bad6d7e34c7c8a69b27e76a3f8b8949b28656d Game layout: view and template: yourgamename.view.php and yourgamename yourgamename.tpl 0 98 684 602 2013-03-06T15:23:33Z Sourisdudesert 1 wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). == Nested blocks == You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it: <pre> (In template file) <!-- BEGIN player --> <div class="miniboard" id="miniboard_{PLAYER_ID}"> <div class="card_places"> <!-- BEGIN card_place --> <div id="card_place_{PLAYER_ID}_{PLACE_ID}"> </div> <!-- END card_place --> </div> </div> <!-- END player --> (In view file) $this->page->begin_block( "mygame_mygame.tpl", "card_place" ); // Nested block must be declared first $this->page->begin_block( "mygame_mygame.tpl", "player" ); foreach( $players as $player_id => $player ) { // Important: nested block must be reset here, otherwise the second player miniboard will // have 8 card_place, the third will have 12 card_place, and so one... $this->page->reset_subblocks( 'card_place' ); for( $i=1; $i<=4; $i++ ) { $this->page->insert_block( "card_place", array( 'PLAYER_ID' => $player_id, 'PLACE_ID' => $i ); } $this->page->insert_block( 'player', array( 'PLAYER_ID' => $player_id ); } </pre> == Javascript templates == For game elements that come and go from the game area, we suggest you to define a Javascript template. A Javascript template is defined in your template file like this: (Reversi Token from Reversi example): <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: a section for javascript templates is already available at the end of your template skeleton file. Then, you can use this javascript template to insert this piece of HTML in your game interface, like this: <pre> dojo.place( this.format_block( 'jstpl_disc', { xy: x+''+y, color: color } ) , 'discs' ); </pre> == How to access game information from .view.php? == From your .view.php, you can access the following: === Access current player id=== <pre> global $g_user; $current_player_id = $g_user->get_id(); </pre> === Access game object === In your view file, "$this->game" contains an instance of your main game class. Example: <pre> // Access to some game elements description described in your "material.inc.php": $my_cards_types = $this->game->card_types; // Access to any (public) method defined in my .game.php file: $result = $this->game->myMethod(); </pre> == Tips: display a nice button == From time to time, you need to display a standard button in your interface. BGA framework provides you a standard button that you can use directly in your interface: <pre> <a href="#" id="my_button_id" class="button"><span>My button label</span></a> </pre> Note: To see it in action, check for example a Coloretto game dfd849b48a11ad56d4e38edef008e0e22a155b02 685 684 2013-03-06T15:23:43Z Sourisdudesert 1 /* Tips: display a nice button */ wikitext text/x-wiki These 2 files work together to provide the HTML layout of your game. Using these 2 files, you specify what HTML is rendered in your game client interface. In <yourgame.tpl>, you can directly write raw HTML that will be displayed by the browser. Example: extract of "hearts_hearts.tpl": <pre> <div id="myhand_wrap" class="whiteblock"> <h3>{MY_HAND}</h3> <div id="myhand"> </div> </div> </pre> == WARNING == Your view and your template are supposed to generate only the BASE layout of the game You shouldn't try to setup the current game situation in the view: this is the role of your Javascript code. Why? Because you'll have to write Javascript code to put game elements in place anyway, and you don't want to write it twice :) Example of things to generate in your view: * The overall layout of your game interface (what is displayed where). * The board and fixed elements on the board (ex: places for cards, squares, ...). Example of things that shouldn't be generate by your view: * Game elements that come and go from the game area. * Game elements that are moving from one place to another. == phplib template system == BGA is using the phplib template system, used for example in PHPbb forums. More details about how to use phplib template system here: http://www.phpbuilder.com/columns/david20000512.php3 == Variables == In your template ("tpl") file, you can use variables. Then in your view (".view.php") file, you fill these variables with value. In the example above, "{MY_HAND}" is a variable. As you can see, a variable is uppercase characters border by "{" and "}". To give a value to this variable in your view.php: Examples: <pre> // Display a translated version of "My hand" at the place of the variable in the template $this->tpl['MY_HAND'] = self::_("My hand"); // Display some raw HTML material at the place of the variable $this->tpl['MY_HAND'] = self::raw( "<div class='myhand_icon'></div>" ); </pre> == Blocks == Using "blocks", you can repeat a piece of HTML from your template several time. You should use "blocks" everytime you have a block of HTML that you have to repeat a big number of time. For example, for Reversi, we have to generate 64 (8x8) squares: <pre> (in reversi_reversi.tpl) <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> <div id="discs"> </div> </div> (in reversi.view.php) $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Explanations: * You specify a block in your template file, using "BEGIN" and "END" keywords. In the example above, we are creating a block named "square". * In your view, you declare your block using "begin_block" method. * Then, you can insert as many block as you want to, using "insert_block" method. The insert_block method takes 2 parameters: * the name of the block to insert. * an associative array you can use to assign values to template variables of this block. In the example above, there are 4 parameters in the block (X, Y, LEFT and TOP). == Nested blocks == You can use nested blocks. In the example below, we are going to add a mini-board for each player of the game, with 4 card places on each of it: <pre> (In template file) <!-- BEGIN player --> <div class="miniboard" id="miniboard_{PLAYER_ID}"> <div class="card_places"> <!-- BEGIN card_place --> <div id="card_place_{PLAYER_ID}_{PLACE_ID}"> </div> <!-- END card_place --> </div> </div> <!-- END player --> (In view file) $this->page->begin_block( "mygame_mygame.tpl", "card_place" ); // Nested block must be declared first $this->page->begin_block( "mygame_mygame.tpl", "player" ); foreach( $players as $player_id => $player ) { // Important: nested block must be reset here, otherwise the second player miniboard will // have 8 card_place, the third will have 12 card_place, and so one... $this->page->reset_subblocks( 'card_place' ); for( $i=1; $i<=4; $i++ ) { $this->page->insert_block( "card_place", array( 'PLAYER_ID' => $player_id, 'PLACE_ID' => $i ); } $this->page->insert_block( 'player', array( 'PLAYER_ID' => $player_id ); } </pre> == Javascript templates == For game elements that come and go from the game area, we suggest you to define a Javascript template. A Javascript template is defined in your template file like this: (Reversi Token from Reversi example): <pre> <script type="text/javascript"> // Templates var jstpl_disc='<div class="disc disccolor_${color}" id="disc_${xy}"></div>'; </script> </pre> Note: a section for javascript templates is already available at the end of your template skeleton file. Then, you can use this javascript template to insert this piece of HTML in your game interface, like this: <pre> dojo.place( this.format_block( 'jstpl_disc', { xy: x+''+y, color: color } ) , 'discs' ); </pre> == How to access game information from .view.php? == From your .view.php, you can access the following: === Access current player id=== <pre> global $g_user; $current_player_id = $g_user->get_id(); </pre> === Access game object === In your view file, "$this->game" contains an instance of your main game class. Example: <pre> // Access to some game elements description described in your "material.inc.php": $my_cards_types = $this->game->card_types; // Access to any (public) method defined in my .game.php file: $result = $this->game->myMethod(); </pre> == Tips: displaying a nice button == From time to time, you need to display a standard button in your interface. BGA framework provides you a standard button that you can use directly in your interface: <pre> <a href="#" id="my_button_id" class="button"><span>My button label</span></a> </pre> Note: To see it in action, check for example a Coloretto game a196c46198d60716daa784c0ab70e4b05830ccef Gamehelplibertalia 0 105 689 2013-03-13T13:45:03Z Peshte 2675 Created page with " == '''How to Play:''' == When it comes down to it, the actual game play of Libertalia is fair simple. Each player has a (mostly) identical set of crew cards. At the start of..." wikitext text/x-wiki == '''How to Play:''' == When it comes down to it, the actual game play of Libertalia is fair simple. Each player has a (mostly) identical set of crew cards. At the start of each round, one player deals out 9 crew members and reads them out loud to the rest of the players. Each other player then finds the same cards and pulls them into their hands. Thus, each round, players are playing with the same crew cards. Each crew card will have a strength (representing what order they choose their booty), and a special ability. Each card also has a tie breaker number, in case 2 players chose the same crew card. Once you have your hand of 9 cards, play begins. Each campaign is broken up into 6 days of looting. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the main pirate ship in decreasing order. At this phase, any special actions that take place during sunrise would happen. Players also will need to use the tie breaker number on any crew cards if 2 or more people played the same crew member. '''''Phase 2: Day''''' The only thing that happens in this phase is any crew member with a day action, may take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Now, each player chooses a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2). So your high ranking cards will get you the best tokens. Also, any dusk actions happen now. '''''Phase 4: Night''''' Once again, the only thing that happens in this phase is crew members may use their Night actions. The difference here though, is any crew members in your cabin, may still use their night actions. So that Barkeep card you played on day 1 of the current round, (his action is to score you 1 doubloon each night) will keep paying you each night of the campaign. Play those night cards early! After the 6th day of the campaign, the round is over. Players resolve any “end of campaign” actions of crew members till alive in their dens (yes, crew members can be killed). Then, each player adds up the value of their booty tokens and moves their score marker ahead on the track. To reset for the next campaign, all crew members used are removed from the game, all doubloons are returned to the bank (except 10) and all booty tokens returned to the bag. The key here is each player should have 3 cards left in their hand. They can use those in the next 2 campaigns. So everyone will have a somewhat different hand going forward. To start the next round, someone draws 6 new crew cards and everyone else takes the matching cards into their hand. Round 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. fed7d8854a5c6ec97249e9f74e82bb1b264bbda4 690 689 2013-03-15T02:06:17Z Jirikki 2655 /* How to Play: */ wikitext text/x-wiki == '''How to Play:''' == In the first round, all players have an identical random set of 9 crew cards. For rounds 2 and 3, each player gets 6 identical cards to add to their 3 left over cards which may be different from the other players. Each crew card has a strength rating and a tie breaker number (representing what order they choose their booty), and a special ability. Crew are arranged with the lowest rating to highest, left to right. The games consists of 3 campaigns, broken up into 6 days of looting. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the main pirate ship in decreasing order. At this phase, any special actions that take place during sunrise would happen. Players also will need to use the tie breaker number on any crew cards if 2 or more people played the same crew member. '''''Phase 2: Day''''' The only thing that happens in this phase is any crew member with a day action, may take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Now, each player chooses a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2). So your high ranking cards will get you the best tokens. Also, any dusk actions happen now. '''''Phase 4: Night''''' Once again, the only thing that happens in this phase is crew members may use their Night actions. The difference here though, is any crew members in your cabin, may still use their night actions. So that Barkeep card you played on day 1 of the current round, (his action is to score you 1 doubloon each night) will keep paying you each night of the campaign. Play those night cards early! After the 6th day of the campaign, the round is over. Players resolve any “end of campaign” actions of crew members till alive in their dens (yes, crew members can be killed). Then, each player adds up the value of their booty tokens and moves their score marker ahead on the track. To reset for the next campaign, all crew members used are removed from the game, all doubloons are returned to the bank (except 10) and all booty tokens returned to the bag. The key here is each player should have 3 cards left in their hand. They can use those in the next 2 campaigns. So everyone will have a somewhat different hand going forward. To start the next round, someone draws 6 new crew cards and everyone else takes the matching cards into their hand. Round 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 4c777c2b3b9d498869f1c54787c435ab9785bb6d 691 690 2013-03-15T02:37:17Z Jirikki 2655 /* How to Play: */ wikitext text/x-wiki == '''How to Play:''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action MUST take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed and action resolved (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 1a01f4d4b2394c68ed674410180a76eeed5666ba 692 691 2013-03-15T02:51:17Z Jirikki 2655 /* How to Play: */ wikitext text/x-wiki == '''How to Play:''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 71d48d5c00cb880cbfa1f5ce67e03c79c5e6fbc4 693 692 2013-03-15T09:27:35Z Jirikki 2655 /* How to Play: */ wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that make Libertalia original is that everyone start with the same set of cards. That's why you'll have to choose carefully the moment to play each card, and to try to read your opponents' strategies. [[Link title]]== '''How to Play:''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 598bdc92946072ab19c78566b75e9983707d93ea Keskustelu:Gamehelpquoridor 1 106 694 2013-03-16T17:33:33Z Sirius136 2706 步步為營(zh) wikitext text/x-wiki [http://heyjude0929.pixnet.net/blog/post/26923277-%5B%E9%81%8A%E6%88%B2%E4%BB%8B%E7%B4%B9%5D%E6%AD%A5%E6%AD%A5%E7%82%BA%E7%87%9Fquoridor] a555fdcb2abc183558136d2652fdb38e4a8911cd Keskustelu:Gamehelptakenoko 1 107 698 2013-03-17T14:19:18Z Maman tuture 2711 Created page with "Guide de référence rapide Chaque tour se compose de 2 étapes: 1) Choisir le Types de temps 2) Achever des objectifs, en effectuant 2 actions différentes Types de temps (..." wikitext text/x-wiki Guide de référence rapide Chaque tour se compose de 2 étapes: 1) Choisir le Types de temps 2) Achever des objectifs, en effectuant 2 actions différentes Types de temps (toujours facultatif) : Soleil - prendre une 3ème action Pluie - cultiver 1 bambou sur n'importe quelle parcelle irrigué Vent - prendre deux actions identiques Tempête - déplacer le panda sur n'importe quelle parcelle,le panda mange un bambou comme d'habitude, si il n'y a pas d'aménagement d'interdiction de panda sur la parcelle Nuages ​​- choisir 1 des 4 autres types de temps pour ce tour ou prendre une puce d'aménagement Aménagements - Des aménagements peuvent être placés que sur des parcelles qui n'ont pas de bambou. Il ne peut y avoir qu'un aménagement par parcelle et une fois placé, ne peut pas être modifié. Actions : Parcelle - Les Parcelles ne peuvent être placés que de façon adjacents à l'étang ou lorsqu'elles sont à côté de 2 autres parcelles en jeu. Irrigation - Les canaux d'irrigation doivent être installé entre deux parcelles. Quand une parcelle est irriguée pour la première fois il y pousse un bambou. Jardinier - Le jardinier fait pousser du bambou sur la parcelle ou il se déplace et sur toutes les parcelles irriguées adjacentes de la même couleur et se déplace en ligne droite Panda - le panda mange un bambou, si il n'y a pas d'aménagement interdiction panda sur la parcelle et se déplace en ligne droite Le jeu se termine quand un certain nombre d'objectifs ont été réalisés. 2 joueurs - 9 objectifs 3 joueurs - 8 objectifs 4 joueurs - 7 objectifs Le premier joueur qui a compléter le nombre requis d'objectifs déclenche la phase finale et obtiens une carte bonus de 2 points supplémentaire. Les autres joueurs ont un tour final. Le score le plus élevé gagne, c'est la calcul du nombre de points sur les objectifs panda qui départage un score a égalité. Si le score reste toujours à égalité, tous les joueurs à égalité ont gagner. b9dd55bf908ecc24830088f1deb125a28474416c Your game state machine: states.inc.php 0 90 702 486 2013-03-23T10:33:02Z Tirix 1962 Corrected 'arg'=>'args' wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "args" PHP method (see below "args" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "args" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "placeWorkers", "description" => clienttranslate('${actplayer} must place some workers'), "descriptionmyturn" => clienttranslate('${you} must place some workers'), "type" => "activeplayer", "args" => "argPlaceWorkers", <================================== HERE "possibleactions" => array( "placeWorkers" ), "transitions" => array( "nextPlayer" => 11, "nextPhase" => 12, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlaceWorkers » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also used values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. 4223897f52eb3a5079cf418baa10823bc4df3b16 714 702 2013-03-28T17:34:44Z Sourisdudesert 1 /* args */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "args" PHP method (see below "args" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "args" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", <================================== HERE "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlaceWorkers » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also used values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. ab9b17a0876a9a2f896473a45b3f21902d84d563 729 714 2013-04-05T02:46:37Z Pikiou 1872 /* args */ returned value from the arg function must be an associative array wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "args" PHP method (see below "args" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "args" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. It should always be an associative array. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", <================================== HERE "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlaceWorkers » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also used values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. 1e0d2ec9b66998ded171afa7edc9b10a50663aa9 Gamehelpcantstop 0 35 703 635 2013-03-24T14:06:08Z Een 3 Chinese translation moved to Chinese wiki wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' Roll 4 standard dice and then choose the sums of any 2 dice to advance your position on the board. You are allowed to use 3 temporary markers to track your progress in columns marked 2 through 12. These markers are commonly white or black. You can continue to roll the dice hoping to roll one of the previous combinations in the three columns selected this turn. When you think you've pushed your luck far enough you say "STOP" then pass the dice to the next player and replace the white markers with your own. If you roll the dice and find no useful combination you fail and your progress is lost. The more common numbers on 2d6 (6,7 and 8) have more steps in their paths while the less common combinations (2 and 12) have fewer. '''The object of the game is''' to reach and cover 3 peaks of these columns! ed20a83da52065513f496d9eca15f358aaa88395 Gamehelpcaylus 0 37 704 108 2013-03-24T14:10:25Z Een 3 Rules link updated in the game page, obsolete information removed wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Gamehelpcoloretto 0 33 705 658 2013-03-24T14:13:28Z Een 3 Move German text to German wiki wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Gamehelpunitedsquare 0 42 706 155 2013-03-24T14:24:23Z Een 3 Adding info about colorblind preference wikitext text/x-wiki ==Goal== The goal is to be the player who has the most squares of his colors. ==Rules== The game contains multicolored squares (red, blue. yellow and green) which appear as 4 colored triangles on the square. Each player on his turn puts a multicolored square next to a square already on the board. The played square (which can be shifted to fit as requested, keeping the order of the colors the same) must be put in a way that creates at least on colored square (not necessarily your color). If it is not possible for the player to put down a square, the turn is passed to the next player. It is also possible to have "non playable" squares which are created when 2 of the same color are connected to the same open square. This is also a strategy to prevent from your opponent from getting more squares of his color. (e.g. if there is a red triangle facing an empty square and i connect to that same empty square another red triangle from another side, the empty square cannot be played and all colors around the "non playable" square will not become squares and won't gain points). ==Ending the game== When all playable squares have been played, the player with the most squares of his color wins the game. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] HAVE A GOOD GAME! ae79857f1a4a6c81a32e01211140a120a3b1acf4 707 706 2013-03-24T14:28:18Z Een 3 wikitext text/x-wiki ==Goal== The goal is to be the player who has the most squares of his colors. ==Rules== The game contains multicolored squares (red, blue. yellow and green) which appear as 4 colored triangles on the square. Each player on his turn puts a multicolored square next to a square already on the board. The played square (which can be shifted to fit as requested, keeping the order of the colors the same) must be put in a way that creates at least on colored square (not necessarily your color). If it is not possible for the player to put down a square, the turn is passed to the next player. It is also possible to have "non playable" squares which are created when 2 of the same color are connected to the same open square. This is also a strategy to prevent from your opponent from getting more squares of his color. (e.g. if there is a red triangle facing an empty square and i connect to that same empty square another red triangle from another side, the empty square cannot be played and all colors around the "non playable" square will not become squares and won't gain points). ==Ending the game== When all playable squares have been played, the player with the most squares of his color wins the game. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''HAVE A GOOD GAME!''' 694ca001b63acf097dcc5297944ab9994a7ef0b8 Gamehelptroyes 0 26 708 665 2013-03-24T14:51:54Z Een 3 Revert to English in the English wiki. German has been moved to the German wiki wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game. == Rules summary == (This rules summary is based on the game help written by [http://boardgamegeek.com/user/pregremlin Andrew Agard] for Board Game Geek, under Creative Commons license. Please see [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] here for details. Thank you Andrew !) ===Initial Placement=== * In clockwise order place a citizen in empty space of one building * Continue in counter clockwise order from last player and so on until all citizens placed ===Game play=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district Have a good game ! fbdfaed8129e77bd038dee3623968940c24674a0 Tutorial gomoku 0 73 709 407 2013-03-24T18:38:25Z Een 3 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( '../../img/gomoku/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { // At the end of the slide, update the intersection dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'no_stone' ); dojo.addClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'stone_' + notif.args.color ); dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); // We can now destroy the stone since it is now visible through the change in style of the intersection dojo.destroy( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ); })); slide.play(); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, send a score update notification to the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 9d1e093ea97321f4d3cd5a98ab0acf194aa21aa5 Gamehelphaggis 0 22 710 48 2013-03-25T05:43:15Z Manchuwok 2352 /* Goal */ wikitext text/x-wiki == Goal == Earn more points than your opponent(s). Points may be earned by emptying your hand before the other players, capturing cards during play, and betting that you will be first to shed all of your cards. == Rules summary == A valid card combination may be a set, a sequence or a bomb: * a "set" is a group of any number of cards of the same rank (ex: one "8", two "5", ...). * a "sequence" is a group of 3 or more cards of the same color with consecutive rank (ex: red 8, red 9, red 10) * a "sequence" can also be a group of 2 or more pairs or larger sets of consecutive rank sharing the same suits between sets (ex: blue 3, green 3, blue 4, green 4, blue 5, green 5) * a "bomb" is one of the following combination (ranked below from lowest to highest): ** 3-5-7-9 (in 4 different colors, "rainbow bomb") ** J-Q ** J-K ** Q-K ** J-Q-K ** 3-5-7-9 (in one color, "suited bomb") Face cards can be used as wild cards to replace any cards in a "set" or a "sequence". Each player starts a round with a Jack, a Queen and a King. These face cards are public. At your turn, you have to play a higher ranking combination with exactly the same type and same number of card that the first combination played. You can also pass. Bombs are an exception: you can play a bomb to beat any combination, except a higher bomb. When all but one player pass in succession, the player who played the highest combination capture all cards played. Important exception: if the highest combination is a bomb, cards are captured by the player with the next higher combination. Then, a new trick starts. The winner of the last trick leads the new trick with any combination. ===Scoring overview=== * At the moment you shed your last card: 5 points per card in the hand of the player who held the most cards. * Cards captured values: 2-4-6-8-10 = 0 point, 3-5-7-9 = 1 point, J = 2 points, Q = 3 points, K = 5 points. * 30 points for a successful "Big bet", 15 points for a successful "Little bet". Points from unsuccessful bets are added to scores of the round's winner and to player(s) who did not bet. 154c77dae5ab153c37efd40d1286011b7c751d2a Gamehelplibertalia 0 105 711 693 2013-03-26T08:59:45Z Kevlarrelic 2786 Corrected some grammar in the introduction. I am an english teacher. It's what I do. :) wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. [[Link title]]== '''How to Play:''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 869176603f34509754ff42de70dd9f95d2127be5 737 711 2013-04-08T16:12:40Z Nalafab 2897 wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. == '''How to Play''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Token are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 47efc3c935f3f435ca534667540ffbd44fe68780 Grade 0 30 712 70 2013-03-26T19:47:56Z Doomquill 2742 /* All grades */ wikitext text/x-wiki Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a mortal. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the angel grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to devilkin or demon. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraph can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherub check that Seraph are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. * '''Demon''': due to a major terms of use violation, this player is not allowed to do anything on the website during a period of time (or forever ...) 8227839171a768ad8ec8be6d69d0eb3af8e1b5d4 Gamehelpseasons 0 43 713 669 2013-03-26T20:45:50Z Doomquill 2742 Clarification of rule: Cards are discarded with no refund if summoned by use of another card, but there is no room for them. wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each card and the crystal owned. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. e983a49ace608ec6898446f1ac07707648c0d7d4 Gamehelpsenet 0 108 715 2013-03-30T14:17:42Z Kara thrace 2819 Created page with " == Summary == Senet ("''The Game of Passing Through the Netherworld''") is well over 5000 yrs old, and is therefore most probably the oldest boardgame ever known. It comes f..." wikitext text/x-wiki == Summary == Senet ("''The Game of Passing Through the Netherworld''") is well over 5000 yrs old, and is therefore most probably the oldest boardgame ever known. It comes from the ancient Egypt, where it was used to amuse people and serve as a religional metaphor for the human souls' journey towards a blessed afterlife. Senet is an elegant and quick blocking game for 2 players, who compete against each other by moving their own pawns across a gameboard of thirty squares. Despite its old age, Senet turns out to be an amusing and tense little game for today's gamers aswell. == Goal == Move your stones (one at a time) on a reverse "S"'-shaped gametrack from the upper left to the lower right corner of the gameboard, where they can leave the gamearea. Be the first to bear all your stones off the gameboard to win! == The four "casting sticks" == The thrown sticks determine, how far you can move a stone during your turn. How to read the sticks: Count the sticks with their light sides up (results can be: 1, 2, 3 or 4). If no light sides show up at all - that's counted as a result of 5. Throwing values of 1, 4 and 5 grant Extra Turns. == Movement rules == If your stone lands on the opponent's unprotected stone, they swap places. You cannot land on a stone of your own colour. Two or more adjacent stones of the same colour protect each other. You always have to try to do a forward move. If this is not possible, you have to move one of your stones backwards. If you have NO valid moves to make at all - you loose your turn. Stones on the last 4 gamefields are never protected and cannot be moved - just borne off. == Special gamefields == Field 26 ('The House of Beauty'): All stones must STOP here on an exact throw, before they can go further. A stone standing here can be borne off the board on an exact throw of 5. Field 27 ('The House of Waters'): This is a pitfall! If you have a stone 'drowning' here, you have to decide whether to return the stone to Field 15 ('The House of Second Life') or to take the sticks and risk to throw a value of 4. (If you succeed to throw a 4 - the stone is borne off). Field 28 ('The House of Three Judges'): A stone standing here, can be borne off the board by an exact throw of 3. Field 29 ('The House of Two Judges'): A stone standing here, can be borne off the board by an exact throw of 2. Field 30 ('The House of Horus'): A stone standing here, can be borne off the board by ANY throw. If a stone is attacked on the last 3 gamefields, it is dropped into the "water" (unless the House of Waters is occupied already). Be the first to bear all your stones off the gameboard! d50b8a464add1615915b7573b6889b85318a277f 716 715 2013-03-30T14:18:07Z Kara thrace 2819 /* Special gamefields */ wikitext text/x-wiki == Summary == Senet ("''The Game of Passing Through the Netherworld''") is well over 5000 yrs old, and is therefore most probably the oldest boardgame ever known. It comes from the ancient Egypt, where it was used to amuse people and serve as a religional metaphor for the human souls' journey towards a blessed afterlife. Senet is an elegant and quick blocking game for 2 players, who compete against each other by moving their own pawns across a gameboard of thirty squares. Despite its old age, Senet turns out to be an amusing and tense little game for today's gamers aswell. == Goal == Move your stones (one at a time) on a reverse "S"'-shaped gametrack from the upper left to the lower right corner of the gameboard, where they can leave the gamearea. Be the first to bear all your stones off the gameboard to win! == The four "casting sticks" == The thrown sticks determine, how far you can move a stone during your turn. How to read the sticks: Count the sticks with their light sides up (results can be: 1, 2, 3 or 4). If no light sides show up at all - that's counted as a result of 5. Throwing values of 1, 4 and 5 grant Extra Turns. == Movement rules == If your stone lands on the opponent's unprotected stone, they swap places. You cannot land on a stone of your own colour. Two or more adjacent stones of the same colour protect each other. You always have to try to do a forward move. If this is not possible, you have to move one of your stones backwards. If you have NO valid moves to make at all - you loose your turn. Stones on the last 4 gamefields are never protected and cannot be moved - just borne off. == Special gamefields == * Field 26 ('The House of Beauty'): All stones must STOP here on an exact throw, before they can go further. A stone standing here can be borne off the board on an exact throw of 5. * Field 27 ('The House of Waters'): This is a pitfall! If you have a stone 'drowning' here, you have to decide whether to return the stone to Field 15 ('The House of Second Life') or to take the sticks and risk to throw a value of 4. (If you succeed to throw a 4 - the stone is borne off). * Field 28 ('The House of Three Judges'): A stone standing here, can be borne off the board by an exact throw of 3. * Field 29 ('The House of Two Judges'): A stone standing here, can be borne off the board by an exact throw of 2. * Field 30 ('The House of Horus'): A stone standing here, can be borne off the board by ANY throw. If a stone is attacked on the last 3 gamefields, it is dropped into the "water" (unless the House of Waters is occupied already). Be the first to bear all your stones off the gameboard! db8e2cd5d5a4e02c8a00c5ef8d237c29b86e91bb How to join BGA developer team? 0 83 717 521 2013-03-30T17:10:00Z Een 3 wikitext text/x-wiki Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf ''''terms & conditions' document''']. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: ''''I agree with the terms & conditions for developers on BGA Studio joined as an attachment''''. And of course, we also encourage you to tell us about which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, we'll discuss by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) Second, once we have discussed together and are fixed on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a 'resources.html' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... that's all, you can start! cecd170b97b99a88d4541f8073f7d553d671aaf7 740 717 2013-04-13T12:27:37Z Een 3 wikitext text/x-wiki Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf ''''terms & conditions' document''']. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: ''''I agree with the terms & conditions for developers on BGA Studio joined as an attachment''''. And of course, we also encourage you to tell us about your developer experience and which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, we'll discuss by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) Second, once we have discussed together and are set on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a ''''resources.html'''' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... well that's all, you can start! See [[Studio#Great.2C_I.27m_in.21_..._How_should_I_start.3F|Great, I'm in! ... How should I start?]] 9c82f85a3c558b3f7a584d879031b343438217f5 Game database model: dbmodel.sql 0 92 718 601 2013-03-31T13:24:39Z Rudolf 2790 /* Default tables */ wikitext text/x-wiki In this file you specify the database schema of your game. This file contains SQL queries that will be executed during the creation of your game table. Note: you can't change the database schema during the game. == Create your schema == To build this file, we recommend you to build the tables you need with the PhpMyAdmin tool (see BGA user guide), and then to export them and to copy/paste the content inside this file. == Default tables == Important: by default, BGA creates 4 tables for your game: global, stats, gamelog, and player. You must not modify the schema of global, stats and gamelog tables (and you must not access them directly with SQL queries in your PHP code). You may add columns to "player" table. This is very practical to add simple values associated with players. Example: <pre> ALTER TABLE `player` ADD `player_reserve_size` SMALLINT UNSIGNED NOT NULL DEFAULT '7'; </pre> For your information, the useful columns of default "player" table are: * player_no: the index of player in natural playing order. * player_id * player_name: (note: you should better access this date with getActivePlayerName() or loadPlayersBasicInfos() methods) * player_score: the current score of the player (displayed in the player panel). You must update this field to update player's scores. * player_score_aux: the secondary score, used as a tie breaker. You must update this field according to tie breaking rules of the game (see also: [[Main_game_logic:_yourgamename.game.php#Manage_player_scores_and_Tie_breaker|Manage_player_scores_and_Tie_breaker]]) == CREATE TABLES == you can create tables, using engine InnoDB <pre> CREATE TABLE IF NOT EXISTS `hands` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `player_id` TINYINT(1) NOT NULL, `1` BOOL NOT NULL DEFAULT 1, `2` BOOL NOT NULL DEFAULT 1, `3` BOOL NOT NULL DEFAULT 1, `4` BOOL NOT NULL DEFAULT 1, `5` BOOL NOT NULL DEFAULT 1, `6` BOOL NOT NULL DEFAULT 1, `7` BOOL NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> '''Note''': if you put comments, you cannot do it in same line than code, <pre> `3` BOOL NOT NULL DEFAULT 1, -- </pre> will comment also starting from `3` BOOL, and code will not be taken into account. == Link == You can add your Db inits in function SetupNewGame() from file 'gamename.game.php' == Errors Log == To trace Database creation, you've got a small summun up in red bars during the game, but details are in logs that you can access in /admin/studio. 2a8a849c1614262e9ba8dbfebfa54de8e18e4677 719 718 2013-03-31T13:26:02Z Rudolf 2790 /* CREATE TABLES */ activated wikitext text/x-wiki In this file you specify the database schema of your game. This file contains SQL queries that will be executed during the creation of your game table. Note: you can't change the database schema during the game. == Create your schema == To build this file, we recommend you to build the tables you need with the PhpMyAdmin tool (see BGA user guide), and then to export them and to copy/paste the content inside this file. == Default tables == Important: by default, BGA creates 4 tables for your game: global, stats, gamelog, and player. You must not modify the schema of global, stats and gamelog tables (and you must not access them directly with SQL queries in your PHP code). You may add columns to "player" table. This is very practical to add simple values associated with players. Example: <pre> ALTER TABLE `player` ADD `player_reserve_size` SMALLINT UNSIGNED NOT NULL DEFAULT '7'; </pre> For your information, the useful columns of default "player" table are: * player_no: the index of player in natural playing order. * player_id * player_name: (note: you should better access this date with getActivePlayerName() or loadPlayersBasicInfos() methods) * player_score: the current score of the player (displayed in the player panel). You must update this field to update player's scores. * player_score_aux: the secondary score, used as a tie breaker. You must update this field according to tie breaking rules of the game (see also: [[Main_game_logic:_yourgamename.game.php#Manage_player_scores_and_Tie_breaker|Manage_player_scores_and_Tie_breaker]]) == CREATE TABLES == you can create tables, using engine InnoDB <pre> CREATE TABLE IF NOT EXISTS `hands` ( `id` INT(10) UNSIGNED NOT NULL AUTO_INCREMENT, `player_id` TINYINT(1) NOT NULL, `1` BOOL NOT NULL DEFAULT 1, `2` BOOL NOT NULL DEFAULT 1, `3` BOOL NOT NULL DEFAULT 1, `4` BOOL NOT NULL DEFAULT 1, `5` BOOL NOT NULL DEFAULT 1, `6` BOOL NOT NULL DEFAULT 1, `7` BOOL NOT NULL DEFAULT 1, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> '''Note''': if you put comments, you cannot do it in same line than code, <pre> `3` BOOL NOT NULL DEFAULT 1, -- activated or not </pre> will comment also starting from `3` BOOL, and code will not be taken into account. == Link == You can add your Db inits in function SetupNewGame() from file 'gamename.game.php' == Errors Log == To trace Database creation, you've got a small summun up in red bars during the game, but details are in logs that you can access in /admin/studio. b66632e64543242efff3183de07348b143ce422d Studio 0 49 720 575 2013-03-31T13:31:10Z Rudolf 2790 /* Other components */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepare you 5 "powerpoint" presentations: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 044e82a5e514797137b0960b427392296a307c6c 738 720 2013-04-13T12:20:43Z Een 3 /* Discover BGA Studio in 5 presentations */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 49bd82e187e809424e09a921d09087648e29041a 739 738 2013-04-13T12:21:12Z Een 3 /* How to join the BGA developer team? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] a639c5429d43182a1d03f41eb76931d1ee5e12ee 741 739 2013-04-13T12:28:58Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we would advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] c43891a136c278b4cc82d5a748fe45170d32aad5 742 741 2013-04-13T12:29:17Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == [[Studio FAQ]] === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 6dcd18e33a284bb4a37226504c1a3706163522ed 743 742 2013-04-13T12:30:15Z Een 3 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please ask them on the [http://forum.boardgamearena.com/viewforum.php?f=12 development forum]. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 2d3092714cdd13c4a5f879634984870268c47799 744 743 2013-04-13T12:31:55Z Een 3 /* Great, I'm in! ... How should I start? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hide. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a <div> element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] f99220401e370693553bd434ceedcfd982b9b526 745 744 2013-04-13T12:34:53Z Een 3 /* BGA Studio game components reference */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 893b70581489f7ff082e842dd3679fc3eebcb54f 746 745 2013-04-13T12:35:51Z Een 3 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). == BGA Studio user guide == This part of the documentation is a user guide for the BGA Studio online development environment. [[Tools and tips of BGA Studio]] [[Practical debugging]] [[Studio back-office]] [[Studio FAQ]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 959f22824e9f3872dbf33d27215a865a48064e04 747 746 2013-04-13T12:36:20Z Een 3 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focus on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). == BGA Studio user guide == This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio back-office]] * [[Studio FAQ]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 9d2e8115976c14cc5292502715452c16a798e93a 749 747 2013-04-13T13:38:01Z Een 3 /* BGA Studio documentation */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). == BGA Studio user guide == This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio back-office]] * [[Studio FAQ]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] d3aee79969dfa85c8c291868d0d0ca64cd86c429 750 749 2013-04-13T13:38:25Z Een 3 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the last information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio back-office]] * [[Studio FAQ]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 33524cca8777e21c41c941b4b654ccc66ead291a 751 750 2013-04-13T13:38:56Z Een 3 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio back-office]] * [[Studio FAQ]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] cedd0f7bf28e85bb5b1e5f8b3c8524a1f6485e41 Game replay 0 109 721 2013-03-31T13:31:32Z Rudolf 2790 Created page with "Game replay is managed by the framework (through replaying javascript notifications). You do not need to do anything special about it in your code." wikitext text/x-wiki Game replay is managed by the framework (through replaying javascript notifications). You do not need to do anything special about it in your code. 2152a2539ed7c895c8f6d92c2a5c3c17b21e2612 Tools and tips of BGA Studio 0 99 722 579 2013-03-31T13:44:45Z Rudolf 2790 Dev Tips wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. == Run from the chat == On BGA Studio, you can directly run a PHP method from the chat. For example, if on your PHP you have this method: <pre> function giveMoneyToPlayer($player_id, $amount) { // Do some stuff } </pre> You can call this method directly from the chat like this: "giveMoneyToCurrentPlayer(2564,2)". == Dev Tips == '''Edit TPL''' <p> To edit TPL with HTML code highlightings in Gedit under Ubuntu: find gtksourceview directory in /usr/share, depending on your version (2.0, 3.0,...) Here it's 3.0, then type in a terminal window: <pre> sudo gedit /usr/share/gtksourceview-3.0/language-specs/html.lang </pre> then find 'globs' section, and change: <pre> <property name="globs">*.html;*.htm;*.tpl</property> </pre> 367271135966a60f7d8667032e164ba115fad7a7 723 722 2013-03-31T13:45:37Z Rudolf 2790 /* Dev Tips */ wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. == Run from the chat == On BGA Studio, you can directly run a PHP method from the chat. For example, if on your PHP you have this method: <pre> function giveMoneyToPlayer($player_id, $amount) { // Do some stuff } </pre> You can call this method directly from the chat like this: "giveMoneyToCurrentPlayer(2564,2)". == Dev Tips == '''Edit TPL''' <p> To edit TPL with HTML code highlightings in Gedit under Ubuntu: find gtksourceview directory in /usr/share, depending on your version (2.0, 3.0,...).<p> Here it's 3.0, then type in a terminal window: <pre> sudo gedit /usr/share/gtksourceview-3.0/language-specs/html.lang </pre> then find 'globs' section, and change: <pre> <property name="globs">*.html;*.htm;*.tpl</property> </pre> dce08d9c2bca925f260d93beb64ba667c5346b0e 724 723 2013-03-31T13:47:11Z Rudolf 2790 /* Dev Tips */ wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. == Run from the chat == On BGA Studio, you can directly run a PHP method from the chat. For example, if on your PHP you have this method: <pre> function giveMoneyToPlayer($player_id, $amount) { // Do some stuff } </pre> You can call this method directly from the chat like this: "giveMoneyToCurrentPlayer(2564,2)". == Dev Tips == '''Edit TPL''' <p> To edit TPL with HTML code highlightings in Gedit under Ubuntu: find gtksourceview directory in /usr/share, depending on your version (2.0, 3.0,...). <br> Here it's 3.0, then type in a terminal window: <pre> sudo gedit /usr/share/gtksourceview-3.0/language-specs/html.lang </pre> then find 'globs' section, and change: <pre> <property name="globs">*.html;*.htm;*.tpl</property> </pre> eabf6d0dd309c1cc92989ad27bf93d72ed46cafa Gamehelphearts 0 52 725 670 2013-04-02T09:29:12Z Deluxus 20 Srca - Hearts wikitext text/x-wiki Srca je igra z kartami za 4 igralce,ki jo igra vsak zase.Uporablja se standardni zavitek 52 kart. '''Moč kart''' A (najvišja) to 2 (najnižja). '''Priprava na igro''' Razdeli se celoten zavitek kart,vsak dobi 13 kart.Po pregledu 13 kart v rokah si izbereš 3 karte in jih podaš drugemu igralcu. Vrstni red podajanja 3 kart: (1 igra) igralcu na svoji levi, (2 igra) igralcu nasproti , (3 igra) igralcu na svoji desni, (4 igra) brez podajanja 3 kart. Vrstni red podajanja se ponavlja dokler igra ni končana. '''Igra''' Po podaji 3 kart, začne igralec ki ima v roki križevo 2. Vsak igralec mora slediti vrženi barvi.Če nima karte z isto bravo, lahko vrže katerokoli karto , katerekoli barve.Izjema : V prvem krogu ne mores vržti srca ali pikove Dame,če nimaš križa. Najvišja karta vzame vzetek.Igralec ki vzame vzetek vrže naslednjo karto. Srca iz roke ne smemo igrati dokler ne pade prvo srce,namesto druge barve.Pikova Dama pa se lahko igra kadarkoli. '''Točkovanje''' Po koncu vsakega kroga,vsako srce šteje -1 in pikova Dama -13. Če ima en igralec vseh 13 scr in pikovo Damo ,torej vse točkovne vzetke piše igralec 0, vsi ostali igralci pa pišejo -26. Kadar eden ali več igralcev pride na 0 ,je igra končana.Igralec z največ točkami zmaga. '''Variante igre''' Normal game je 100 točk. Quick game je 75 točk. 0fd9b27e12cc0e7c59b96f292587355a8c5a1f53 726 725 2013-04-02T09:31:13Z Deluxus 20 Srca wikitext text/x-wiki Srca je igra z kartami za 4 igralce,ki jo igra vsak zase.Uporablja se standardni zavitek 52 kart. '''Moč kart''' A (najvišja) do 2 (najnižja). '''Priprava na igro''' Razdeli se celoten zavitek kart,vsak dobi 13 kart.Po pregledu 13 kart v rokah si izbereš 3 karte in jih podaš drugemu igralcu. Vrstni red podajanja 3 kart: (1 igra) igralcu na svoji levi, (2 igra) igralcu nasproti , (3 igra) igralcu na svoji desni, (4 igra) brez podajanja 3 kart. Vrstni red podajanja se ponavlja dokler igra ni končana. '''Igra''' Po podaji 3 kart, začne igralec ki ima v roki križevo 2. Vsak igralec mora slediti vrženi barvi.Če nima karte z isto bravo, lahko vrže katerokoli karto , katerekoli barve.Izjema : V prvem krogu ne mores vržti srca ali pikove Dame,če nimaš križa. Najvišja karta vzame vzetek.Igralec ki vzame vzetek vrže naslednjo karto. Srca iz roke ne smemo igrati dokler ne pade prvo srce,namesto druge barve.Pikova Dama pa se lahko igra kadarkoli. '''Točkovanje''' Po koncu vsakega kroga,vsako srce šteje -1 in pikova Dama -13. Če ima en igralec vseh 13 src in pikovo Damo ,torej vse točkovne vzetke piše igralec 0, vsi ostali igralci pa pišejo -26. Kadar eden ali več igralcev pride na 0 ,je igra končana.Igralec z največ točkami zmaga. '''Variante igre''' Normal game je 100 točk. Quick game je 75 točk. 0af950fd94f906473c28158f37df8967fd0fe100 Game interface logic: yourgamename.js 0 88 727 682 2013-04-02T15:45:08Z Pikiou 1872 /* Tooltips */ addTooltip and addTooltipHtml get a node id, not a node element wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 87b3d23d52c75153c79cb900bcb8e2160e9aa782 728 727 2013-04-03T08:25:15Z Sourisdudesert 1 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 68e1bb2388e4f34a0f12444cca0b8e51a5072093 Scrollmap 0 110 730 2013-04-08T12:26:19Z Sourisdudesert 1 Created page with "Scrollmap is a BGA client side component to display an infinite game area. In many games, players are building the main game area with tiles or cards. Examples: * Carcassonne..." wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In many games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... For tiles placement games when player are building , the board can be "infinite" cf4c11de919be13bbfdf77bb3e3610adf144ca01 732 730 2013-04-08T12:35:49Z Sourisdudesert 1 wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> f5870573733c23a47fbe7d00e3fe90f27b6a22f4 733 732 2013-04-08T12:54:34Z Sourisdudesert 1 /* How to use Scrollmap */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. 2ab80d98e101873cfc58e10bc94bde0a24ac0ee3 734 733 2013-04-08T12:55:28Z Sourisdudesert 1 /* Scrollable area layers */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. 44346c9a67f3757698a18a3d64803996ff5685d5 735 734 2013-04-08T12:58:51Z Sourisdudesert 1 /* Scrollable area layers */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. e748822609d8cf4e816469a8a8b5eb157d898a16 736 735 2013-04-08T13:00:00Z Sourisdudesert 1 /* How to use Scrollmap */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. eafd93396078275005cc7f744097c11c87c551a8 Stock 0 97 731 701 2013-04-08T12:33:20Z Sourisdudesert 1 /* Using stock: a simple example */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_themeurl+'img/hearts/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly 9052c361ca1eb2db288e379288080c355a3d2799 First steps with BGA Studio 0 111 748 2013-04-13T13:11:19Z Een 3 Created page with "== Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * you..." wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as WinSCP for example) # Check than in your home folder you have: #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == That's all! Now you know about the basics of updating your game on the studio and testing your changes! 1f6096bf4d1c068292d042c0363a4fa2c952d352 First steps with BGA Studio 0 111 752 748 2013-04-13T13:40:33Z Een 3 /* Commit your changes */ wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as WinSCP for example) # Check than in your home folder you have: #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make an change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do it. == That's all for your first steps! == Now you know about the basics of updating your game on the studio and testing your changes. For more information on the specifics of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 2859dd460cb4e3a04a35a5cddab674d8644efd66 753 752 2013-04-13T13:41:04Z Een 3 /* That's all for your first steps! */ wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as WinSCP for example) # Check than in your home folder you have: #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make an change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do it. == That's all! == Now you know about the basics of updating your game on the studio and testing your changes. For more information on the specifics of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 4eb92e9d60a7f84791aa477080ee79603d07e860 754 753 2013-04-13T13:41:24Z Een 3 /* That's all! */ wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as WinSCP for example) # Check than in your home folder you have: #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make an change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do it. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specifics of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 745c7b8b5751da1fe807c4d42c47542fbcf1d6ad 775 754 2013-04-21T21:50:04Z Mlgx 2882 wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as WinSCP for example) # Check that your home folder contains : #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains : #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make a change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do so. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specificites of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 99a92d5b3f86aa127525af0e3fc76e9abca6fef9 Gamehelpstoneage 0 21 755 135 2013-04-14T18:57:40Z Spacediver 718 /* Feed your people */ wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). Special rules for game with 2 players only: * no more than 1 player may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field Special rules for game with 3 players only: * no more than 2 players may occupy each of the following places: forest, clay pit, quarry, and river. * no more than 2 players may occupy '''the group''' of following places: tool maker, hut, and field ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * '''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage (or choose not) to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). * each resource that a player has on his player board scores 1 point 5921b3c6e17808ee318a02fc06ff06030a85b8f7 Gamehelplibertalia 0 105 756 737 2013-04-15T07:51:11Z Mantra 2943 /* How to Play */ wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. == '''How to Play''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Tokens are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 1db47195e1202cd4d33fe0ee8d989c0b20b42c52 Gamehelpcantstop 0 35 757 703 2013-04-17T06:10:09Z Spheniscine 2908 wikitext text/x-wiki Can't Stop is a very easy and fast game! '''How to play''' Roll four standard dice. Divide them into two groups of two dice however you like, and advance on the appropriate track(s). You are allowed to use 3 temporary markers to track your progress in columns marked 2 through 12. These markers are commonly white or black. You can continue to roll the dice hoping to roll one of the previous combinations in the three columns selected this turn. When you think you've pushed your luck far enough you say "STOP" then pass the dice to the next player and replace the white markers with your own. If you roll the dice and find no useful combination you fail and your progress is lost. The more common numbers on 2d6 (6,7 and 8) have more steps in their paths while the less common combinations (2 and 12) have fewer. '''The object of the game is''' to reach and cover 3 peaks of these columns! f9c605bcf2b10f1518626fd7ce451fa440ce681c Main game logic: yourgamename.game.php 0 86 758 688 2013-04-18T14:32:20Z Pikiou 1872 /* Game states and active players */ Precision on setPlayersMultiactive wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new feException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. b9878cf2203fdcacd211b41f75d0f03be44bb80a Tutorial reversi 0 57 759 389 2013-04-19T12:36:14Z Garcia1968 2900 wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "this.addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "states.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". d0f5fd5942831d1805c5cf65817d5dfdb0bbfba6 Gamehelpgearnpiston 0 112 760 2013-04-19T13:01:05Z Pikiou 1872 Created page with "=QUICK RULES REFERENCE= ==ROUND SEQUENCE AND PHASES== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ==LOCATION ACTIONS== *Back Alley: Choo..." wikitext text/x-wiki =QUICK RULES REFERENCE= ==ROUND SEQUENCE AND PHASES== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ==LOCATION ACTIONS== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. ==REQUIREMENTS OF A COMPLETE AUTOMOBILE== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ==GAME END== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ==SCORING== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility 936ccfde729bfdf1af1090698d33ea21dacd50cd 761 760 2013-04-19T13:18:14Z Pikiou 1872 Smaller titles wikitext text/x-wiki ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility 9585ac256cef85265388481e8ff6f7409cf7dd8b 762 761 2013-04-19T13:20:28Z Pikiou 1872 Introduction sentence wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility 82f66c7de4d9a33ed8b6b26c761118881b4366ce 763 762 2013-04-19T21:22:24Z Jukka h 2977 /* LOCATION ACTIONS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility 29595ee77bed2a188e6083d0ce260b31cc174dce 764 763 2013-04-20T12:31:19Z Jukka h 2977 /* QUICK RULES REFERENCE */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action 0016c51475c81cf2ea3c5b6365bbd3fa798d1a44 765 764 2013-04-20T12:40:27Z Jukka h 2977 /* BEGINNER TIPS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 9d79b0fec2d87cb081a91d56ee045243f429fad6 776 765 2013-04-23T07:33:19Z Lolinthedark 3013 /* LOCATION ACTIONS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires a Motor, Fuel supply for each Motor, Steering, Gear and two Axles. * Each type of part (electric, gasoline, steam) requires a Motor and Supply of same type. * Each part on the top row requires a part on the bottom row and vice versa. * Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 350628121ba1bd4cd19bcc14ce0137a554a154d1 Scrollmap 0 110 766 736 2013-04-20T16:06:32Z Een 3 /* How to use Scrollmap */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } /** Move arrows **/ #movetop,#moveleft,#moveright,#movedown { display: block; position: absolute; background-image: url('../../img/common/arrows.png'); width: 32px; height: 32px; } #movetop { top: 0px; left: 50%; background-position: 0px 32px; } #moveleft { top: 50%; left: 0px; background-position: 32px 0px; } #moveright { top: 50%; right: 0px; background-position: 0px 0px; } #movedown { bottom: 0px; left: 50%; background-position: 32px 32px; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. 9bf1328defe1d592e843c1d52e57a4fb02f03b19 767 766 2013-04-20T16:08:47Z Een 3 /* How to use Scrollmap */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. 8be1c95eca27e0a1f53cb9de14f88a8f439ebe46 768 767 2013-04-20T16:13:01Z Een 3 wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. == Move arrows == To show move arrows around the scrollmap to enable click based navigation in addition to drag and drop, add these lines to your CSS <pre> /** Move arrows **/ #movetop,#moveleft,#moveright,#movedown { display: block; position: absolute; background-image: url('../../img/common/arrows.png'); width: 32px; height: 32px; } #movetop { top: 0px; left: 50%; background-position: 0px 32px; } #moveleft { top: 50%; left: 0px; background-position: 32px 0px; } #moveright { top: 50%; right: 0px; background-position: 0px 0px; } #movedown { bottom: 0px; left: 50%; background-position: 32px 32px; } </pre> Then, in your javascript, define the following functions <pre> onMoveTop : function( evt ) { console.log( "onMoveTop" ); evt.preventDefault(); this.scrollmap.scroll( 0, 300 ); }, onMoveLeft : function( evt ) { console.log( "onMoveLeft" ); evt.preventDefault(); this.scrollmap.scroll( 300, 0 ); }, onMoveRight : function( evt ) { console.log( "onMoveRight" ); evt.preventDefault(); this.scrollmap.scroll( -300, 0 ); }, onMoveDown : function( evt ) { console.log( "onMoveDown" ); evt.preventDefault(); this.scrollmap.scroll( 0, -300 ); }, </pre> And connect onclick events on the arrows to these functions in your javascript setup <pre> dojo.connect( $('movetop'), 'onclick', this, 'onMoveTop' ); dojo.connect( $('moveleft'), 'onclick', this, 'onMoveLeft' ); dojo.connect( $('moveright'), 'onclick', this, 'onMoveRight' ); dojo.connect( $('movedown'), 'onclick', this, 'onMoveDown' ); </pre> a5779da4f4a8368a11f8c98f9602ae601684abc8 769 768 2013-04-20T16:23:55Z Een 3 wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. == Enable move arrows == To show move arrows icons around the scrollmap in order to enable click based navigation in addition to drag and drop, add these lines to your CSS: <pre> /** Move arrows **/ #movetop,#moveleft,#moveright,#movedown { display: block; position: absolute; background-image: url('../../img/common/arrows.png'); width: 32px; height: 32px; } #movetop { top: 0px; left: 50%; background-position: 0px 32px; } #moveleft { top: 50%; left: 0px; background-position: 32px 0px; } #moveright { top: 50%; right: 0px; background-position: 0px 0px; } #movedown { bottom: 0px; left: 50%; background-position: 32px 32px; } </pre> In your javascript, define the following functions: <pre> onMoveTop : function( evt ) { console.log( "onMoveTop" ); evt.preventDefault(); this.scrollmap.scroll( 0, 300 ); }, onMoveLeft : function( evt ) { console.log( "onMoveLeft" ); evt.preventDefault(); this.scrollmap.scroll( 300, 0 ); }, onMoveRight : function( evt ) { console.log( "onMoveRight" ); evt.preventDefault(); this.scrollmap.scroll( -300, 0 ); }, onMoveDown : function( evt ) { console.log( "onMoveDown" ); evt.preventDefault(); this.scrollmap.scroll( 0, -300 ); }, </pre> And connect onclick events on the arrows to these functions in your javascript setup <pre> dojo.connect( $('movetop'), 'onclick', this, 'onMoveTop' ); dojo.connect( $('moveleft'), 'onclick', this, 'onMoveLeft' ); dojo.connect( $('moveright'), 'onclick', this, 'onMoveRight' ); dojo.connect( $('movedown'), 'onclick', this, 'onMoveDown' ); </pre> 5ab6faa990a4c4f66cd6235581c04cef32f984db 780 769 2013-04-26T13:53:16Z Een 3 wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. == Enable move arrows == To show move arrows icons around the scrollmap in order to enable click based navigation in addition to drag and drop, add these lines to your CSS: <pre> /** Move arrows **/ #movetop,#moveleft,#moveright,#movedown { display: block; position: absolute; background-image: url('../../img/common/arrows.png'); width: 32px; height: 32px; } #movetop { top: 0px; left: 50%; background-position: 0px 32px; } #moveleft { top: 50%; left: 0px; background-position: 32px 0px; } #moveright { top: 50%; right: 0px; background-position: 0px 0px; } #movedown { bottom: 0px; left: 50%; background-position: 32px 32px; } </pre> In your javascript, define the following functions: <pre> onMoveTop : function( evt ) { console.log( "onMoveTop" ); evt.preventDefault(); this.scrollmap.scroll( 0, 300 ); }, onMoveLeft : function( evt ) { console.log( "onMoveLeft" ); evt.preventDefault(); this.scrollmap.scroll( 300, 0 ); }, onMoveRight : function( evt ) { console.log( "onMoveRight" ); evt.preventDefault(); this.scrollmap.scroll( -300, 0 ); }, onMoveDown : function( evt ) { console.log( "onMoveDown" ); evt.preventDefault(); this.scrollmap.scroll( 0, -300 ); }, </pre> And connect onclick events on the arrows to these functions in your javascript setup <pre> dojo.connect( $('movetop'), 'onclick', this, 'onMoveTop' ); dojo.connect( $('moveleft'), 'onclick', this, 'onMoveLeft' ); dojo.connect( $('moveright'), 'onclick', this, 'onMoveRight' ); dojo.connect( $('movedown'), 'onclick', this, 'onMoveDown' ); </pre> == Enable scrollmap zone extension == This is optional, when there can be unused screen space under the scrollmap that a player might want to use. Add this in your .tpl after the scrollmap div (the matching css rules has already been defined): <pre> <div id="map_footer" class="whiteblock"> <a href="#" id="enlargedisplay">&darr;&nbsp;&nbsp;{LABEL_ENLARGE_DISPLAY}&nbsp;&nbsp;&darr;</a> </div> </pre> In your javascript, define the following function: <pre> onIncreaseDisplayHeight: function( evt ) { console.log( '$$$$ Event : onIncreaseDisplayHeight' ); evt.preventDefault(); var cur_h = toint( dojo.style( $('map_container'), 'height' ) ); dojo.style( $('map_container'), 'height', ( cur_h+300 ) + 'px' ); }, </pre> and connect them to the 'enlargedisplay' link in your setup: <pre> dojo.connect( $('enlargedisplay'), 'onclick', this, 'onIncreaseDisplayHeight' ); </pre> 2a5e97072af863315ae4bad3c5110f7b7a36fd68 781 780 2013-04-26T13:57:14Z Een 3 /* Enable scrollmap zone extension */ wikitext text/x-wiki Scrollmap is a BGA client side component to display an infinite game area. In some games, players are building the main game area with tiles or cards. Examples: * Carcassonne * Saboteur * Takenoko * Taluva * ... Of course this cause an additional difficulty for the adaptation, because we have to display an infinite game area into a finite space on the screen. This is where Scrollmap component can help you. == Scrollmap in action == If you want to see how Scrollmap looks like, please try "Saboteur" or "Takenoko" games on BGA, or watch a game in progress. In both games, you can see that there are arrow controls around the main game area, so that players can use them to scroll the view. You can also drag'n'drop the game area to scroll. == How to use Scrollmap == At first, don't forget to add "ebg/scrollmap" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/scrollmap" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Scrollmap object: <pre> constructor: function(){ console.log('yourgame constructor'); // Scrollable area this.scrollmap = new ebg.scrollmap(); </pre> Now, open your template (TPL) file and add this HTML code: <pre> <div id="map_container"> <div id="map_scrollable"></div> <div id="map_surface"></div> <div id="map_scrollable_oversurface"></div> <a id="movetop" href="#"></a> <a id="moveleft" href="#"></a> <a id="moveright" href="#"></a> <a id="movedown" href="#"></a> </div> </pre> There are also some lines to add to your CSS stylesheet. Please note that you can adapt it to your needs, especially the default width of the scrollable area: <pre> /** Scrollable area **/ #map_container { position: relative; width: 100%; height: 400px; overflow: hidden; } #map_scrollable, #map_scrollable_oversurface { position: absolute; top: 205px; left: 315px; } #map_surface { position: absolute; top: 0px; left: 0px; width: 100%; height: 100%; cursor: move; } #map_footer { text-align: center; } </pre> Finally, to link your HTML code with your Javascript, place this in your Javascript "Setup" method: <pre> // Make map scrollable this.scrollmap.create( $('map_container'),$('map_scrollable'),$('map_surface'),$('map_scrollable_oversurface') ); this.scrollmap.setupOnScreenArrows( 150 ); </pre> This is it! Now, you should see on your game interface a scrollable game area. This is not really impressive though, because you didn't add anything on the game area yet. This is the next step. == Scrollable area layers == There are 2 - and only 2 - places where you should place your HTML stuff in your scrollable area: * inside "map_scrollable" div * inside "map_scrollable_oversurface" div The difference is very important: "map_scrollable" is beneath the surface that is used to drag'n'drop the game area, and "map_scrollable_oversurface" is above this surface. In practice: * If some element on the game area need to be clicked (or any kind of user interaction), you should place it in map_scrollable_oversurface, otherwise no click can reach it. * If some element on the game area don't need to be clicked, you'd better place it in "map_scrollable", so it is possible to drag'n'drop the game area from a point on this element. Of course, all layers are scrolled synchronously. Tips: in some situation, it's also useful to place a game element on map_scrollable and a corresponding invisible element over the surface to manage the interactions. Example: when an interactive element must be placed beneath a non interactive element for display reason. == Positioning elements on game area == All elements on the game are must be absolute positioned (with "top" and "left" attributes). By default, the game area is centered on 0,0 coordinates. == Enable move arrows == To show move arrows icons around the scrollmap in order to enable click based navigation in addition to drag and drop, add these lines to your CSS: <pre> /** Move arrows **/ #movetop,#moveleft,#moveright,#movedown { display: block; position: absolute; background-image: url('../../img/common/arrows.png'); width: 32px; height: 32px; } #movetop { top: 0px; left: 50%; background-position: 0px 32px; } #moveleft { top: 50%; left: 0px; background-position: 32px 0px; } #moveright { top: 50%; right: 0px; background-position: 0px 0px; } #movedown { bottom: 0px; left: 50%; background-position: 32px 32px; } </pre> In your javascript, define the following functions: <pre> onMoveTop : function( evt ) { console.log( "onMoveTop" ); evt.preventDefault(); this.scrollmap.scroll( 0, 300 ); }, onMoveLeft : function( evt ) { console.log( "onMoveLeft" ); evt.preventDefault(); this.scrollmap.scroll( 300, 0 ); }, onMoveRight : function( evt ) { console.log( "onMoveRight" ); evt.preventDefault(); this.scrollmap.scroll( -300, 0 ); }, onMoveDown : function( evt ) { console.log( "onMoveDown" ); evt.preventDefault(); this.scrollmap.scroll( 0, -300 ); }, </pre> And connect onclick events on the arrows to these functions in your javascript setup <pre> dojo.connect( $('movetop'), 'onclick', this, 'onMoveTop' ); dojo.connect( $('moveleft'), 'onclick', this, 'onMoveLeft' ); dojo.connect( $('moveright'), 'onclick', this, 'onMoveRight' ); dojo.connect( $('movedown'), 'onclick', this, 'onMoveDown' ); </pre> == Enable scrollmap zone extension == This is optional, when there can be unused screen space under the scrollmap that a player might want to use. Add this in your .tpl after the scrollmap div (the matching css rules has already been defined): <pre> <div id="map_footer" class="whiteblock"> <a href="#" id="enlargedisplay">&darr;&nbsp;&nbsp;{LABEL_ENLARGE_DISPLAY}&nbsp;&nbsp;&darr;</a> </div> </pre> In your javascript, define the following function: <pre> onIncreaseDisplayHeight: function( evt ) { console.log( '$$$$ Event : onIncreaseDisplayHeight' ); evt.preventDefault(); var cur_h = toint( dojo.style( $('map_container'), 'height' ) ); dojo.style( $('map_container'), 'height', ( cur_h+300 ) + 'px' ); }, </pre> and connect them to the 'enlargedisplay' link in your setup: <pre> dojo.connect( $('enlargedisplay'), 'onclick', this, 'onIncreaseDisplayHeight' ); </pre> In your view.php file, define the template variable LABEL_ENLARGE_DISPLAY so that it gets substituted with appropriate translatable text: <pre> $this->tpl['LABEL_ENLARGE_DISPLAY'] = self::_("Enlarge display"); </pre> 3589839894165d82db552742333c42b99f573a78 Zone 0 113 770 2013-04-20T17:16:41Z Een 3 Created page with "The Zone component is meant to organise items of the same type inside a predefined space. == Zone in action == If you want to see how Zone works, please try "Can't Stop" or ..." wikitext text/x-wiki The Zone component is meant to organise items of the same type inside a predefined space. == Zone in action == If you want to see how Zone works, please try "Can't Stop" or "Niagara" on BGA, or watch a game in progress or game replay. In Can't Stop, zone is used to display the bhikkus ascending the mountain when there is more than one on the same space (diagonal mode). In Niagara, it is used to display the canoes over the circles going down the river (custom mode). == How to use Zone == At first, don't forget to add "ebg/zone" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/zone" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Zone object: <pre> constructor: function(){ console.log('yourgame constructor'); // Zone control this.myZone = new ebg.scrollmap(); </pre> Now, in your template file, you must add a div that will host this zone: <pre> <div id="my_zone"></div> </pre> And set its width (and optionnaly, height and position) in CSS: <pre> #my_zone { width: 100px; } </pre> Then in your Javascript setup, attach your Zone component to the div and define its properties : <pre> zone.create( this, 'my_zone', <item_width>, <item_height> ); zone.setPattern( <mode> ); </pre> * <item_width> is an integer for the width of the objects you want to organise in the zone * <item_height> is an integer for the height of the objects you want to organise in the zone * <mode> is one of 'grid' (objects will be put on lines from top left to bottom right, wrapping when there is not enough space left on the line) 'diagonal' (objects will be organised on a top left to bottom right diagonal, overlapping each other) or 'custom' (objects will be organised depending upon their number and the coordinates that you provide - see below) 3b1acb84955007d42770352ff5eaec3220ecff2f 771 770 2013-04-20T17:38:13Z Een 3 wikitext text/x-wiki The Zone component is meant to organise items of the same type inside a predefined space. == Zone in action == If you want to see how Zone works, please try "Can't Stop" or "Niagara" on BGA, or watch a game in progress or game replay. In Can't Stop, zone is used to display the bhikkus ascending the mountain when there is more than one on the same space (diagonal mode). In Niagara, it is used to display the canoes over the circles going down the river (custom mode). == How to use Zone == At first, don't forget to add "ebg/zone" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/zone" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Zone object: <pre> constructor: function(){ console.log('yourgame constructor'); // Zone control this.myZone = new ebg.scrollmap(); </pre> Now, in your template file, you must add a div that will host this zone: <pre> <div id="my_zone"></div> </pre> And set its width (and optionnaly, height and position) in CSS: <pre> #my_zone { width: 100px; } </pre> Then in your Javascript setup, attach your Zone component to the div and define its properties : <pre> zone.create( this, 'my_zone', <item_width>, <item_height> ); zone.setPattern( <mode> ); </pre> * <item_width> is an integer for the width of the objects you want to organise in the zone * <item_height> is an integer for the height of the objects you want to organise in the zone * <mode> is one of 'grid' (objects will be put on lines from top left to bottom right, wrapping when there is not enough space left on the line) 'diagonal' (objects will be organised on a top left to bottom right diagonal, overlapping each other) or 'custom' (objects will be organised depending upon their number and the coordinates that you provide - see below) Now your zone is ready to be used! After creating an object that you want to add to the zone as a classic HTML template (dojo.place / this.format_block), you can simply use: <pre> zone.placeInZone( <object_id>, <weight> ); </pre> * <object_id> is the string identifier for your object 'my_object_id' * <weight> is an optional parameter used to sort items To remove an item, use: <pre> zone.removeFromZone( <object_id>, <destroy?>, <to> ); </pre> * <object_id> is the string identifier for your object 'my_object_id' * <destroy?> is a boolean indicating if the object should be destroyed after being removed * <to> is the destination the object must be slided to when being removed (before being eventually destroyed) You can also: * remove all items from the zone (and destroy them) using zone.removeAll(); * get the number of items in your zone using zone.getItemNumber(); * get an array of the ids of all the items using zone.getAllItems(); f3e8cbbc363d78eb8dcb46bf70ffb2710985f7bb 772 771 2013-04-20T17:42:40Z Een 3 wikitext text/x-wiki The Zone component is meant to organise items of the same type inside a predefined space. == Zone in action == If you want to see how Zone works, please try "Can't Stop" or "Niagara" on BGA, or watch a game in progress or game replay. In Can't Stop, zone is used to display the bhikkus ascending the mountain when there is more than one on the same space (diagonal mode). In Niagara, it is used to display the canoes over the circles going down the river (custom mode). == How to use Zone == At first, don't forget to add "ebg/zone" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/zone" /// <==== HERE ], </pre> Then, declare a new variable in your class for the Zone object: <pre> constructor: function(){ console.log('yourgame constructor'); // Zone control this.myZone = new ebg.scrollmap(); </pre> Now, in your template file, you must add a div that will host this zone: <pre> <div id="my_zone"></div> </pre> And set its width (and optionnaly, height and position) in CSS: <pre> #my_zone { width: 100px; } </pre> Then in your Javascript setup, attach your Zone component to the div and define its properties : <pre> zone.create( this, 'my_zone', <item_width>, <item_height> ); zone.setPattern( <mode> ); </pre> * <item_width> is an integer for the width of the objects you want to organise in the zone * <item_height> is an integer for the height of the objects you want to organise in the zone * <mode> is one of 'grid' (objects will be put on lines from top left to bottom right, wrapping when there is not enough space left on the line) 'diagonal' (objects will be organised on a top left to bottom right diagonal, overlapping each other) or 'custom' (objects will be organised depending upon their number and the coordinates that you provide - see below) Now your zone is ready to be used! After creating an object that you want to add to the zone as a classic HTML template (dojo.place / this.format_block), you can simply use: <pre> zone.placeInZone( <object_id>, <weight> ); </pre> * <object_id> is the string identifier for your object 'my_object_id' * <weight> is an optional parameter used to sort items To remove an item, use: <pre> zone.removeFromZone( <object_id>, <destroy?>, <to> ); </pre> * <object_id> is the string identifier for your object 'my_object_id' * <destroy?> is a boolean indicating if the object should be destroyed after being removed * <to> is the destination the object must be slided to when being removed (before being eventually destroyed) You can also: * remove all items from the zone (and destroy them) using zone.removeAll(); * get the number of items in your zone using zone.getItemNumber(); * get an array of the ids of all the items using zone.getAllItems(); == Custom mode == If you want complete control on how your objects are laid out inside the Zone, you can determine their coordinates after their number when being added to the zone. Here is how to do it, when defining your Zone properties in the javascript setup: <pre> this.zone.setPattern( 'custom' ); this.zone.itemIdToCoords = function( i, control_width ) { if( i%8==0 ) { return { x:1,y:19, w:60, h:30 }; } else if( i%8==1 ) { return { x:30,y:38, w:60, h:30 }; } else if( i%8==2 ) { return { x:42,y:8, w:60, h:30 }; } else if( i%8==3 ) { return { x:5,y:58, w:60, h:30 }; } else if( i%8==4 ) { return { x:5,y:24, w:60, h:30 }; } else if( i%8==5 ) { return { x:35,y:43, w:60, h:30 }; } else if( i%8==6 ) { return { x:47,y:13, w:60, h:30 }; } else if( i%8==7 ) { return { x:10,y:63, w:60, h:30 }; } }; </pre> afea6db99aa0caa0117e35848e3982a5cefa003e Keskustelu käyttäjästä:Elisha 3 114 773 2013-04-21T20:36:30Z Elisha 2997 Camp Phillip wikitext text/x-wiki == Camp Phillip == It is in Wautoma 7a30187f839af147b50b8b04b297d320f6416207 Tools and tips of BGA Studio 0 99 774 724 2013-04-21T21:46:57Z Rudolf 2790 /* Run from the chat */ MoneyToPlayer instead ToCurrentPleyer wikitext text/x-wiki == Starting a game in one click == To start a game: * Create a new table with your game. * If you want to play a game with 3 players, specify that you want a maximum of 3 players at this table. * Click on "Express Start". == Stopping a game in one click == * Click on the "quit" icon on the top right of the screen. * Click on "Express Stop". == Switching between users == when running a game on Studio, you can use the little red arrow near each player's name to open a new tab with this player's perspective. == Access to game database == Immediately at the bottom of the game area, the "Go to game database" link is an immediate access to the PhpMyAdmin tool to view/edit the tables of the current game. == Save & restore state == Using links of this section, you can save the complete current (database) state of your game, then restore it later. This is particularly useful when you want to develop a part of the game that is difficult to reproduce: you just have to save the situation just before, and then restore it until this part works fine. We provide you 3 "slots": 1, 2 and 3. This way, you can save 3 different game situations. Limits: * the "restore" function does not work anymore when the game is over. * a saved situation from a given table cannot be restored in another table. * when you "restore" a situation, the current browser page is refreshed to reflect the updated game situation, but you have to refresh you other tabs/pages manually. == Input/Output debugging section == This section shows you: * The AJAX calls made by your game interface to the game server. AJAX calls (outputs) begins with ">" * The notifications received by your game interface. Notifications (inputs) begins with "<". Note: if you click on some notification title, you can resend it immediately to the user interface. == Run from the chat == On BGA Studio, you can directly run a PHP method from the chat. For example, if on your PHP you have this method: <pre> function giveMoneyToPlayer($player_id, $amount) { // Do some stuff } </pre> You can call this method directly from the chat like this: "giveMoneyToPlayer(2564,2)". == Dev Tips == '''Edit TPL''' <p> To edit TPL with HTML code highlightings in Gedit under Ubuntu: find gtksourceview directory in /usr/share, depending on your version (2.0, 3.0,...). <br> Here it's 3.0, then type in a terminal window: <pre> sudo gedit /usr/share/gtksourceview-3.0/language-specs/html.lang </pre> then find 'globs' section, and change: <pre> <property name="globs">*.html;*.htm;*.tpl</property> </pre> 7575406d5970f76c7920d09a72393b76aa7a139e How to join BGA developer team? 0 83 777 740 2013-04-24T20:19:05Z Een 3 ² wikitext text/x-wiki '''--- Notice ---''' We have received a lot of positive feedback on the Studio, and lots of developers have registered. We are very happy about it, but it comes with a cost in time spent answering emails and posts on the forum. So please bear with us if we are slow to answer. As soon as we can, we will. Thanks again for your interest! Registering is done by e-mail at '''studio(at)boardgamearena.com'''. Please provide the following information: * your player user name on BGA * your developer user name to be used on “BGA Studio” (for technical reasons, no space, number or special character); * your real name; * your e-mail address; * your postal address. We also drafted a quick [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf ''''terms & conditions' document''']. It's very light, so as to get to the fun part faster. To be valid, the registration e-mail must contain this document as an attachment, with the following sentence in the mail body: ''''I agree with the terms & conditions for developers on BGA Studio joined as an attachment''''. And of course, we also encourage you to tell us about your developer experience and which games you would love to develop on BGA! == Ok, I registered, what do I get? == First, we'll discuss by e-mail the names of some of the games we have an agreement to develop, so that you can tell us 'Hey, this game is great! I want to develop it!' (please keep these names confidential, or it would ruin our tradition to make players guess the name of the next game on the forum) Second, once we have discussed together and are set on a game you'll take charge of, we'll create your studio account and you will get: * one login / password to access files through SFTP * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. Once logged in to your SFTP root folder, you will find: * a ''''resources.html'''' file with the URLs to use and some extra connection information * three folders containing the three simple games provided as examples * a folder for the game you will be developing initialized after our 'EmptyGame' template, providing the game structure (and comments! and examples!) Then... well that's all, you can start! See [[Studio#Great.2C_I.27m_in.21_..._How_should_I_start.3F|Great, I'm in! ... How should I start?]] f33949896ce62ca89e3592d2b284019fa26aa725 Studio FAQ 0 53 778 582 2013-04-25T20:01:50Z Een 3 wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. de2963afe15123299fc130eb797b6dec2336f852 779 778 2013-04-25T20:03:11Z Een 3 /* Should I use images free from copyright? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == An extra deployment action from the BGA administrators is needed to activate new game options and statistics. Please contact us. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 0a29445f5efe57020b3a57c7cd1dc368e323f8a7 Gamehelpjaipur 0 115 782 2013-04-28T02:32:04Z Coolpapa 3036 Rules summary added wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - "Take" a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - "Trade" for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - "Sell" cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. 961a44a284c0dcaf904d1e738bef062cb01b38a7 786 782 2013-04-28T22:47:55Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. e36707fc2bb42423ae195ad7e075bda6793b9811 787 786 2013-04-28T22:48:26Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. b784c03317aaeaa85595a08a0437840627e181f5 788 787 2013-04-28T22:48:54Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. afbf2081c76a32ae7bfaa6e3df778a7baa8af5a4 789 788 2013-04-28T22:49:12Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. cbe563e749c1ac4f0a52a3254a38d2a63809aa4f 790 789 2013-04-28T22:50:59Z Zkiller 1253 wikitext text/x-wiki <nowiki>In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game.</nowiki> dccbdb4a9810a9fbf0a6ad10688939e8cc8c6141 791 790 2013-04-28T22:51:35Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: - Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. - Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. - Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: - Three piles of goods tokens are depleted, OR - The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. cbe563e749c1ac4f0a52a3254a38d2a63809aa4f 792 791 2013-04-28T22:53:13Z Zkiller 1253 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: 1. Take a single card or all the camels in the face up row: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) The card row is then replenished from the deck so it always has 5 cards at the start of a turn. 2. Trade for 2 or more goods cards from the row. You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. The cards your traded are added to the card row, so it will still have 5 cards. 3. Sell cards to earn points: You may sell one or more cards to earn points, but all the cards you sell must be the same color. If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). The sold goods cards are discarded. The game continues until either: Three piles of goods tokens are depleted, OR The card row cannot be replenished. At that point, players total the points they have on their earned goods tokens and bonus tokens. In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. Whichever player earns more points total wins the round. After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. 4252f07fcba2193fdc6fca5d3eb71abba575b82f Gamehelpseasons 0 43 783 713 2013-04-28T08:34:30Z Tolcso 3039 wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each played card and the crystal owned and substracting 5 points for each power card remaining in hand. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. 3ea25e6e940596dbd22fb862bd8e668ff6747768 784 783 2013-04-28T08:47:52Z Tolcso 3039 wikitext text/x-wiki Season is a game of generating points (crystal) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: Star - increase the maximum card player can summon (max 15) Elements - Gain an energy of the element shown (water, earth, air, fire) Numbers - Gain the number of Crystals as indicated by the number Square card - Draw a card Dice with frames surrounding - Allow user to transmute energy into crystal (depend on which part of the game is at) Dots - Indicate how fast the marker progress through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There is 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each played card and the crystal owned and substracting 5 points for each power card remaining in hand. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. Difficulty levels: Apprentice wizard level In order to make the game easier to learn, there are “pre-constructed” sets of nine power cards. Instead of taking step 1 of the first game phase, each player gets one of these sets. Magician level Only the power cards numbered 1 to 30 are in play. These cards have easy to grasp effects and will allow you to slowly discover the world of Seasons. Archmage level All 50 kinds of power cards are in play. The cards numbered 31 through 50 have more complex effects than the basic cards, but will allow you to extend the fun of playing by discovering new effects and combinations. 7981328360c518298628e927fdde9a7e10945645 Gamehelpthermopyles 0 116 785 2013-04-28T18:45:18Z Boardgametravel 3042 Created page with "The rules for Thermopyles are on BoardGameTravel.com: http://boardgametravel.com/cardboard-sun-2013-thermopyles-by-touko-tahkokallio/" wikitext text/x-wiki The rules for Thermopyles are on BoardGameTravel.com: http://boardgametravel.com/cardboard-sun-2013-thermopyles-by-touko-tahkokallio/ 14db865c2a24bd8154124a531e44b068594fcc71 Studio 0 49 793 751 2013-04-29T13:09:31Z Sourisdudesert 1 wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio back-office]] * [[Studio FAQ]] == BGA Developer team organization == * [[Steps to create a BGA game]] * [[Post-release phase]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] 09360c43e84cfb0fda9ec982df2ba3017f8ff4a1 Steps to create a BGA game 0 117 794 2013-04-29T13:33:24Z Sourisdudesert 1 Created page with " Here's a summary of the different steps you would follow when developing a game with BGA Studio. {| class="wikitable" |- ! Step !! How to reach this ste?p !! What happened d..." wikitext text/x-wiki Here's a summary of the different steps you would follow when developing a game with BGA Studio. {| class="wikitable" |- ! Step !! How to reach this ste?p !! What happened during the step? |- | Initial || [[How to join BGA developer team?]] || You discuss with us to choose your game |- | Assigned || You choosed a game || You can start the development of the game |- | Pre-alpha || You've started to write some piece of code || You develop the game. During this phase, we can assist you with the framework and give you some pieces of advice. |- | Alpha || You tell us that your development is finished || We are reviewing the game on our side and with you, and help you to finalize some details to polish the game. |- | Private beta || We give a "go" || The game is incorporate in BGA global "package", and is published on BGA preproduction server. On this platform, the publisher, the designer, we and you can test the game together and separately. We help you to take into account remarks from the publisher and the designer. |- | Public beta || The adaptation is approved by the publiher || We find together a good launch date for the game, we announce the game on BGA news, and then player can start to play! During the first days, it is common that some bugs are reported by players, and you can fix them following the instructions in [[Post-release phase]]. |- | Gold || The game is stable on BGA || Congrats! You can still modify and optimize things following the instructions in [[Post-release phase]]. |} fd83647a0d63ee8c73d0d758a652a4fa882a89e9 Post-release phase 0 118 795 2013-04-29T13:55:59Z Sourisdudesert 1 Created page with " Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify..." wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package: Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. 00719bc33fbb31e5b9d0411273ca6934503dc879 796 795 2013-04-29T13:59:21Z Sourisdudesert 1 wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. 3d6fce514ac532c20726ecc2298f198752a759a4 797 796 2013-04-29T14:02:35Z Sourisdudesert 1 wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. 06912ed7642ce5075efe6e07129b9d6f7ef509e9 798 797 2013-04-29T14:09:13Z Sourisdudesert 1 /* What can be modified after release? */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. 1773893f8e863b61a600264dce70ad22267ac46c 799 798 2013-04-29T14:11:38Z Sourisdudesert 1 wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. ===Major changes=== 700ca998e835f73a7be0541d4dc13a951a7c837e 800 799 2013-04-29T14:14:49Z Sourisdudesert 1 /* Major changes */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) 1e2c8a7c70f2e90310a06b99dbed1777b7587ac9 801 800 2013-05-02T12:01:04Z Sourisdudesert 1 /* BGA packages: when my updates will be visible by players? */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == To submit your changes, you just have to commit your work from BGA Studio backoffice, as you did during the development phase. Warning: as soon as you commited your changes, we assume that your code is ready to deploy '''anytime''' on BGA. Consequently, please do not commit a development in progress. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) a894a30459e913bc4e1ea7a84648f20439ac5bef Post-release phase 0 118 802 801 2013-05-02T12:01:12Z Sourisdudesert 1 /* How to submit changes? */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == To submit your changes, you just have to commit your work from BGA Studio backoffice, as you did during the development phase. '''Warning''': as soon as you commited your changes, we assume that your code is ready to deploy '''anytime''' on BGA. Consequently, please do not commit a development in progress. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) 3a40bfbd6133af2d943ac159671a956ff80bb408 803 802 2013-05-02T12:05:14Z Sourisdudesert 1 /* What can be modified after release? */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == To submit your changes, you just have to commit your work from BGA Studio backoffice, as you did during the development phase. '''Warning''': as soon as you commited your changes, we assume that your code is ready to deploy '''anytime''' on BGA. Consequently, please do not commit a development in progress. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) ===Post-release and commit=== As said above: for games already published on BGA, we assume that your code is ready to deploy as soon as you commited your changes. In consequence, please: * Do not commit until you finished and tested your updates. * Do not commit a development in progress. * As a rule of thumb: do not commit something that will bring the game in a state that should not be seen by players. cdf42364d5f044ab669cc89ed65e8b7284dfffd0 824 803 2013-05-13T12:18:44Z Sourisdudesert 1 /* Changes that breaks the games in progress */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == To submit your changes, you just have to commit your work from BGA Studio backoffice, as you did during the development phase. '''Warning''': as soon as you commited your changes, we assume that your code is ready to deploy '''anytime''' on BGA. Consequently, please do not commit a development in progress. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. === Updating Statistics === You should be careful when updating a statistics: * If you want to add a new statistics, please refer to the paragraph above ("changes that breaks the games in progress"). * If you want to update a statistic, please update it and do not remove/create another one. Otherwise, the statistic won't keep the same ID and players will lost all the historical statistics data. * If your game is published on BGA, please don't remove any statistics (historical data will be lost). === Updating string to translate === When you update a string that has been marked to be translatable, please keep in mind that all current translations done by the BGA community will be lost. Consequently, when you are about to modify a string to be translated (after release), please ask you the following questions: * Is it just an English mispelling? In this case, it is better to fix the English translation of the string than the original string to be translated. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) ===Post-release and commit=== As said above: for games already published on BGA, we assume that your code is ready to deploy as soon as you commited your changes. In consequence, please: * Do not commit until you finished and tested your updates. * Do not commit a development in progress. * As a rule of thumb: do not commit something that will bring the game in a state that should not be seen by players. 6e886bcf9df1b7d39c3fbacf9c439f64bb7126b7 825 824 2013-05-13T12:21:13Z Sourisdudesert 1 /* Updating string to translate */ wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == To submit your changes, you just have to commit your work from BGA Studio backoffice, as you did during the development phase. '''Warning''': as soon as you commited your changes, we assume that your code is ready to deploy '''anytime''' on BGA. Consequently, please do not commit a development in progress. == BGA packages: when my updates will be visible by players? == BGA website is updated with "packages". When needed, we build a new package with all games and release a new version of BGA with this package. It means that your updates won't be visible by players until a new package is build and released on the website. Usually, there is less than 2 weeks between 2 packages, so it's quick. BUT, if you detect some major bug in your game, please warn us immediately so we can decide what to do. Usually, we do the following: * We can do a "hotfix": you send us a very little change and we fix the website immediately. This is only possible if you change only the PHP side. The good news is that most blocking bugs are on PHP side - the client side bugs are most of the time solved by a page refresh. Of course, we don't hotfix minor bugs. * We can build another package. Of course, it's better to fix several bugs in each package, in order we don't have to build another package right after the first one :) * If the situation is critical, we can suspend the game from BGA waiting for the update. Of course, we particularly care of your game during the days after your game is available on BGA. During this period of time, there's no problem to build package and to hotfix just for your game, and this is done in close collaboration with you. You just have to inform us when you're ready to publish a new version. Note: don't forget that in any case, you need to commit your changes to made them available for the next package. Modifications that are not commited are not included in the packages. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one BGA package, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress, '''as soon as you commit your update'''. * Thus, during the package delivery, we will block the game and let game in progress end before publishing the new version. === Updating Statistics === You should be careful when updating a statistics: * If you want to add a new statistics, please refer to the paragraph above ("changes that breaks the games in progress"). * If you want to update a statistic, please update it and do not remove/create another one. Otherwise, the statistic won't keep the same ID and players will lost all the historical statistics data. * If your game is published on BGA, please don't remove any statistics (historical data will be lost). === Updating string to be translated === When you update a string that has been marked to be translatable, please keep in mind that all current translations done by the BGA community will be lost. Consequently, when you are about to modify a string to be translated (after release), please ask you the following questions: * Is it just an English misspelling? In this case, it is better to fix the English translation of the string than the original string to be translated. * Has the meaning of the string changed? If yes, you HAVE to change the original string in order to invalidate all translations that has been done already. * Is there a similar string already used elsewhere in my game? In this case, you'd better use it again to enjoy immediately all translations already available. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * Discuss with you about the release date of the next BGA package. * Pay attention to your game when publishing the package. * And eventually, publish a news about it :) ===Post-release and commit=== As said above: for games already published on BGA, we assume that your code is ready to deploy as soon as you commited your changes. In consequence, please: * Do not commit until you finished and tested your updates. * Do not commit a development in progress. * As a rule of thumb: do not commit something that will bring the game in a state that should not be seen by players. 356393196feb99d952606ceadf545a08b97dfa02 Your game state machine: states.inc.php 0 90 804 729 2013-05-05T21:14:19Z Rudolf 2790 /* args */ argPlayerTurn instead argPlaceWorker :) wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "args" PHP method (see below "args" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "args" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. It should always be an associative array. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", <================================== HERE "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlayerTurn » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also used values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. 3812e44b110471e5e6b8940d1dd683d52a7c7946 Studio function reference 0 55 805 448 2013-05-06T17:07:55Z Sourisdudesert 1 /* Client side (Javascript functions) */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : DEPRECATED (see connectClass below) ; connectClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player e95dad6f948fc435abb59e380167b6a3cabc6080 806 805 2013-05-06T17:08:23Z Sourisdudesert 1 /* Client side (Javascript functions) */ wikitext text/x-wiki This page references useful server side and client side functions (and some interesting class variables), so that nobody needs to reinvent the wheel (unless he wants to). This list is not exhaustive, in particular functions already well described by comments in the 'EmptyGame' game template may not be described again below. == Server side (PHP functions) == == Client side (Javascript functions) == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side. ; slideToObject: function( mobile_obj, target_obj, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one : Animate a slide of the DOM object referred to by domNodeToSlide from its current position to the xpos, ypos relative to the object referred to by domNodeToSlideTo. ; slideToObjectPos: function( mobile_obj, target_obj, target_x, target_y, duration, delay ) : Return an dojo.fx animation that is sliding a DOM object from its current position over another one at the given coordinates relative to the target object. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. ; addTooltip( node, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to the DOM node. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. ; addTooltipHtml( node, html, delay ); : Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). ; addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay ); : Add a simple text tooltip to all the DOM nodes set with this cssClass. Only one of 'helpString' or 'actionString' must be used. _() must be used for the text to be marked for translation. : NB: all concerned nodes must have IDs to get tooltips ; addTooltipHtmlToClass( cssClass, html, delay ); : Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). : NB: all concerned nodes must have IDs to get tooltips ; addEventToClass: function( cssClassName, eventName, functionName ) : DEPRECATED (please use connectClass below) ; connectClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName ; addStyleToClass: function( cssClassName, cssProperty, propertyValue ) : Same as dojo.style(), but for all the nodes set with the specified cssClassName ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; checkAction: function( action, nomessage ) : Check if player can do the specified action by taking into account: _ current game state & _ interface locking : return true if action is authorized : return false and display an error message if not (display no message if nomessage is specified) ; showMessage: function( msg, type ) : Show an information message during a few seconds at the top of the page : Type can be 'error' or 'info' ; this.scoreCtrl[ player_id ].incValue( score_delta ); : Adds score_delta (positive or negative integer) to the current score value for player 160e684dfe21df32501ea756dd20aa6dad8f0b36 Gamehelpniagara 0 119 807 2013-05-08T10:38:45Z Drstuey 3099 Created page with "Players paddle canoes along the Niagara river to collect gems of five different colors and bring them safely back to shore without being swept over the waterfall. To win, a pl..." wikitext text/x-wiki Players paddle canoes along the Niagara river to collect gems of five different colors and bring them safely back to shore without being swept over the waterfall. To win, a player must return either: * five different colored gems * four gems of the same color * seven gems of any color The tie break is the value of the gems - the gems closer to the waterfall have a higher value. In order the gems are: : Gem - Color - Value : Amethyst - Violet - 1 : Diamond - Clear - 2 : Amber - Yellow - 3 : Sapphire - Blue - 4 : Ruby - Pink - 5 Players have a set of 7 paddle cards which they play to determine the speed of their canoes and the speed of the river. 4ff0d66fb1e334b75f2b1d160b47252872bad10c 808 807 2013-05-08T10:44:52Z Drstuey 3099 wikitext text/x-wiki Players paddle canoes along the Niagara river to collect gems of five different colors and bring them safely back to shore without being swept over the waterfall. To win, a player must return either: * five different colored gems * four gems of the same color * seven gems of any color The tie break is the value of the gems - the gems closer to the waterfall have a higher value. In order the gems are: : Gem - Color - Value : Amethyst - Violet - 1 : Diamond - Clear - 2 : Amber - Yellow - 3 : Sapphire - Blue - 4 : Ruby - Pink - 5 Players have a set of 7 paddle cards which they play to determine the speed of their canoes and the speed of the river. All players must select the card they will play before the players then move their canoes in turn order. Once a card has been played it is not available to play again until all 7 cards have been played. 572aa6c5a9b8af31652c9722ecc4b01d8201d376 839 808 2013-05-26T07:59:53Z Drstuey 3099 wikitext text/x-wiki Players paddle canoes along the Niagara river to collect gems of five different colors and bring them safely back to shore without being swept over the waterfall. To win, a player must return either: * five different colored gems * four gems of the same color * seven gems of any color The tie break is the value of the gems - the gems closer to the waterfall have a higher value. In order the gems are: : Gem - Color - Value : Amethyst - Violet - 1 : Diamond - Clear - 2 : Amber - Yellow - 3 : Sapphire - Blue - 4 : Ruby - Pink - 5 Players have a set of 7 paddle cards which they play to determine the speed of their canoes and the speed of the river. All players must select the card they will play before the players then move their canoes in turn order. Once a card has been played it is not available to play again until all 7 cards have been played. After all the players have moved their canoes the river moves causing all the canoes on the river to be swept downstream. The river moves the same number of places as the lowest value paddle card played plus or minus a modifier depending on the weather. At the beginning of the game the weather is set to 0 so there is no modification but each player also has a cloud card as part of their set of paddle cards that allows them to adjust the weather and hence make the river speed up or slow down. The number on the paddle card determines the number of spaces that the canoe moves - it costs 2 paddle points to pick up (or offload) a gem. Thus if a player chose their 5 card they could move their canoe 5 spaces *or* they could move their canoe 3 spaces and pick up a gem (or they could offload a gem and move their canoe 3 spaces). Both canoes may be moved with the same paddle card - both canoes are moved the same amount, the paddles points are not split between the canoes. When both canoes are out of the water only one can be launched in that turn. If both canoes are in the water they must both be moved using the full value of the paddle points - a player cannot chose not to move one canoe and must always use the full value of the paddle points. However if one of the canoes is out of the water a player may choose not to launch it. Canoes may only move upstream or downstream in a single turn - it is not permitted to change direction in the middle of the turn. However one canoe could move upstream and the second one downstream. Players may steal gems from other canoes, but only after moving upstream and only if they finish on the same space as the other player (and only if they have an empty canoes and hence space to load the gem). Turn order is therefore crucial for stealing. 11578909bb91ae614ac1f3ad026c9b251bda1aa5 Steps to create a BGA game 0 117 809 794 2013-05-09T18:54:43Z Sourisdudesert 1 wikitext text/x-wiki Here's a summary of the different steps you would follow when developing a game with BGA Studio. {| class="wikitable" |- ! Step !! How to reach this ste?p !! What happened during the step? |- | Initial || [[How to join BGA developer team?]] || You discuss with us to choose your game |- | Assigned || You choosed a game || You can start the development of the game |- | Pre-alpha || You've started to write some piece of code || You develop the game. During this phase, we can assist you with the framework and give you some pieces of advice. |- | Alpha || You tell us that your development is finished || The game is incorporate in BGA global "package", and is published on BGA preproduction server. We are reviewing the game on our side and with you, and help you to finalize some details to polish the game. |- | Private beta || We give a "go" || On preproduction platform, the publisher, the designer, we and you can test the game together and separately. We help you to take into account remarks from the publisher and the designer. |- | Public beta || The adaptation is approved by the publiher || We find together a good launch date for the game, we announce the game on BGA news, and then player can start to play! During the first days, it is common that some bugs are reported by players, and you can fix them following the instructions in [[Post-release phase]]. |- | Gold || The game is stable on BGA || Congrats! You can still modify and optimize things following the instructions in [[Post-release phase]]. |} 7ec2dce7dd3162238a0135745a42c2144a65ca5b Translations 0 94 810 675 2013-05-10T19:54:03Z Een 3 /* On client side (Javascript) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short strings. * The BGA translation policy is to be flexible on grammar... We prefer to write "player gets 1 coin(s)" than write two versions of the same string for plural and singular - it reduces the number of strings to translate. * Instead of writing nice strings like "With the effect of ZZZ, player XXX gets a new YYY", which is very difficult to translate, write strings like "ZZZ: XXX gets YYY". == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> '''WARNING:''' in Javascript strings to translate, you should never use '\n', '\t' or such, as it will break the translation bundle and result in all the Javascript translation to fail. In any case, the strings will result in HTML code, and such character codes won't have any impact on the HTML rendering. You should use HTML markup instead. == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new feException( self::_('You must choose 3 cards'), true); // ... notice the use of "true" parameter that signal that this exception is "expected". In theory, all exception that are excepted should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new feException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'], true ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php 22624734ef6e061272e85bfa5c3a50f5129dd586 836 810 2013-05-21T21:36:02Z Tilalilalou 2792 /* On server side (PHP) */ wikitext text/x-wiki Using BGA Studio, the game you create is ready to be translated to each language by the BGA community. To make this possible, you only need to specify which string must be translated and how to combine them. == How translation works? == When developing your game, all strings must be in English. Strings must be coherent with the English version of the game. Before the release of the game, BGA team will do the French translation of the game. After the release of the game, the BGA players community will translate the game in every language. == What should be translated? == Every text that can be visible by the player when the game is running normally. This includes tooltips, texts on cards, error messages, ... This does NOT include error messages that are not supposed to happened (unexpected errors). == Focus on translating notifications == Usually, translating a website is simple: you just call a function on every string you have to translate, and the string is translated in the player's language. On Board Game Arena, this is exactly the same with the "_( string )" function. However, there is one difference on BGA: notifications. The server is sending notifications to players, and most of the time the notifications are the same for every players, no matter what language each player is using. This is why notifications are translated on client side in the proper language, even if the strings are defined on server side. == WARNING: how to make sure your strings will be translated == For each game, our translation tool is doing a full scan of the code, looking for translator markers like "_()" or "clientranslate()"... (see below the list of translation markers). If your original string is not "physically" inside one of this marker, it won't be translated. <pre> // Examples: the following strings will be translated: var mystring_translated = _("my string"); // JS $mystring_translated = self::_("my string"); // PHP $mystring_translated = sprintf( _("my string with an %s argument"), $argument ); // PHP // Examples: the following strings WILL NOT be translated: $my_string = "my string"; $not_translated = self::_( $my_string ); // The original string is not bordered by a translator marker => no translation $not_translated = self::_( sprintf( "my string with a %s argument", $argument ) ); // Same thing </pre> == How to not make translators crazy ;) == * When you need the same string twice, try to reuse exactly the same string (with the same case) to minimize the number of strings. * Do not mark as translatable a game element that does not have to be translated (ex: if the name of a monster on a card is "Zzzzz", maybe there's no need to translate it). * Words does not come in the same order in each language. Thus, when you have to translate a string with an argument, do not write something like: <pre>_("First part of the string, ").$argument.' '._("second part of the string")</pre> Write instead: <pre>sprintf( _("First part of the string, %s second part of the string"), $argument )</pre> (or the equivalent "dojo.string.substitute" in Javascript) * When translators are going to translate your game, the most difficult task for them is to get the context of the string to be translated. The more the string is a short insignificant string, the more difficult is the task for them. As a rule of thumb, try to avoid insignificant short strings. * The BGA translation policy is to be flexible on grammar... We prefer to write "player gets 1 coin(s)" than write two versions of the same string for plural and singular - it reduces the number of strings to translate. * Instead of writing nice strings like "With the effect of ZZZ, player XXX gets a new YYY", which is very difficult to translate, write strings like "ZZZ: XXX gets YYY". == On client side (Javascript) == On client side, things are quite simple: you just have to use the "_()" function for all strings you want to translate. Examples: <pre> // Get a string in player's language: var translated = _("original english string"); // Get a string in player's language with parameter: var translated = dojo.string.substitute( "You can pick ${p} cards and discard ${d}", { p: 2, d: 4 } ); </pre> '''WARNING:''' in Javascript strings to translate, you should never use '\n', '\t' or such, as it will break the translation bundle and result in all the Javascript translation to fail. In any case, the strings will result in HTML code, and such character codes won't have any impact on the HTML rendering. You should use HTML markup instead. == On server side (PHP) == On PHP side, you can use 3 different functions to specify that a string must be translated. '''clienttranslate( "my string to translate" ):''' This function is '''transparent''': it will return the original English string without any change. It's only purpose is to mark this string as "must be translated", and to make sure the translated version of the string will be available on client side. In general, you use clienttranslate: * On your states.inc.php, for field "description" and "descriptionmyturn". <pre> "description" => clienttranslate('${card_name}: ${actplayer} must discard 4 identical energies'), </pre> * On "material.inc.php", when defining texts for game material that must be displayed on client side. <pre> $this->card_types = array( 1 => array( 'name' => clienttranslate("Amulet of Air"), // Thus, we can use "_( card_name )" on Javascript side. </pre> * When sending a notification with "notifyAllPlayers" or "notifyPlayer", for the game log string and all game log arguments that need a translation. <pre> // A game log string with no argument: self::notifyAllPlayers( 'pickLibraryCards', clienttranslate("Everyone draw cards from his library"), array() ); </pre> Translating arguments is a little bit more complex. It is using the "i18n" special argument as below: <pre> // In the following example, we translate the game log itself, but also the "card_name" argument: self::notifyAllPlayers( 'winPoints', clienttranslate('${card_name}: ${player_name} gains ${points} point(s)'), array( 'i18n' => array( 'card_name' ), // <===== We specify here that "card_name" argument must be transate 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'points' => $points, 'card_name' => $this->card_types[8]['name'] // <==== Here, we provide original English string. ) ); </pre> '''self::_( "my string to translate" ):''' This function returns a string translated in the language of CURRENT user (ie: player who send the request to the server) (be careful, this is NOT the active player). Most of the time, you don't need to translate strings on server side, except on the following 3 situations: * When throwing an exception because the player did a forbidden move. <pre> // This will display a translatable red message to the player that just do some wrong action: throw new BgaUserException( self::_('You must choose 3 cards') ); // ... notice the use of BgaUserException that signals that this exception is "expected". In theory, all exception that are expected should be translated. </pre> * In "yourgame.view.php", when creating the labels for the game interface used in your template (.tpl) file. <pre> $this->tpl['CARDS_FOR_YEAR_2'] = self::_("Your cards for year II"); </pre> * Eventually, in your material.inc.php, if for example you need to use some string elements in your exceptions. <pre> // In material.inc.php, $this->energies[n]['nametr'] has been created with the self::_() method. This we can do this: throw new BgaUserException( self::_("To execute this action you need more: ").' '.$this->energies[$resource_id]['nametr'] ); </pre> * Eventually, in your "getAllDatas" PHP method, as the data return by this method is used only by current user. '''totranslate( "my string to translate" ):''' This function works exactly like 'clienttranslate', except it tells BGA that the string is not needed on client side. You should not use this function, except on the following cases: * Statistics name in stats.inc.php * Option names and option values name in gameoptions.inc.php febee0f3048f5cfd727355754ea4e0137867df32 Gamehelpdragonheart 0 31 811 146 2013-05-11T23:04:14Z Sternenfee83 3127 /* Cards */ wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 3 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. In case of a tie, the player with the Great Dragon wins. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are nine types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the deck. The ship cards are then discarded. The third time that three ship cards are played ends the game and the opponent takes one final turn. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. 550055d41a907bf6a87ac1a5a0c8521d91f520f9 Gamehelpgearnpiston 0 112 812 776 2013-05-12T03:16:10Z Jirikki 2655 /* REQUIREMENTS OF A COMPLETE AUTOMOBILE */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. d635ec38db2d845cad6db71b86daa4af043eacba 813 812 2013-05-12T03:27:28Z Jirikki 2655 /* GAME END */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors * +1 VP for each part in one largest continuous area * -1 VP for each point of Volatility ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. de7e7b26782be0047213175f41810b1b0377928a 814 813 2013-05-12T03:41:47Z Jirikki 2655 /* SCORING */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Plan actions. *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two of each are worth either 1, 2 or 3 based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 847321eb680103116893e558e7ce2f0a3ed8c89e 815 814 2013-05-12T03:48:02Z Jirikki 2655 /* ROUND SEQUENCE AND PHASES */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (steal a part from another player), **or Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two of each are worth either 1, 2 or 3 based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 8fe5b6392bbbcfaf3f233a60ad6a0113dd062127 816 815 2013-05-12T03:49:44Z Jirikki 2655 /* LOCATION ACTIONS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two of each are worth either 1, 2 or 3 based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 8546ba6ce30fb1fa8df1d50a9f6b5cc6ad76e451 817 816 2013-05-12T03:55:30Z Jirikki 2655 /* SCORING */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the parts next to the game or from the New Parts stack. *Junk Yard: Take up to 2 Junk Parts, either from the parts next to the game or from the New Junk Parts stack. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 9fc3e1b36e15b47dae5af304be9c4209c53f62e1 818 817 2013-05-12T04:04:02Z Jirikki 2655 /* LOCATION ACTIONS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the face up parts or from the top of the stack. *Junk Yard: Take up to 2 Junk Parts, either from the 3 face up parts or from the top of the stack. If a face up part is chosen, it is replaced before the second is chosen. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. * Make sure to build often enough so you don't end up with a full hand and unspent actions. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. * Reading the actual rules helps also. 99947660d1dd9a476d1e853fcf5db23ae7914722 819 818 2013-05-12T04:26:14Z Jirikki 2655 /* BEGINNER TIPS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the face up parts or from the top of the stack. *Junk Yard: Take up to 2 Junk Parts, either from the 3 face up parts or from the top of the stack. If a face up part is chosen, it is replaced before the second is chosen. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (motor and fuel tank), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. If three people are building the same color of engine it is going to be very hard for all of them to succeed. * Make sure to build often enough because you can hold a maximum of 5 blueprints at a time. Also if you aren't in the first two spots in the workshop you aren't going to get enough actions to finish your car. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. The junk yard also has only the 5 kinds of parts required to finish a vehicle so if you are missing a key part you are much more likely to find it here than blindly drawing from the patent office. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. c24c871220e1a21fa8a444d63e17d584192eb61a 820 819 2013-05-12T04:29:04Z Jirikki 2655 /* REQUIREMENTS OF A COMPLETE AUTOMOBILE */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the face up parts or from the top of the stack. *Junk Yard: Take up to 2 Junk Parts, either from the 3 face up parts or from the top of the stack. If a face up part is chosen, it is replaced before the second is chosen. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (a motor and at least one fuel tank of the same color), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank but there can be extra fuel tanks. * Colored improvements require an engine of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. If three people are building the same color of engine it is going to be very hard for all of them to succeed. * Make sure to build often enough because you can hold a maximum of 5 blueprints at a time. Also if you aren't in the first two spots in the workshop you aren't going to get enough actions to finish your car. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. The junk yard also has only the 5 kinds of parts required to finish a vehicle so if you are missing a key part you are much more likely to find it here than blindly drawing from the patent office. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. 900b2c3d01813d95f9b661bee2133ac6c411da5d 821 820 2013-05-12T04:33:08Z Jirikki 2655 /* REQUIREMENTS OF A COMPLETE AUTOMOBILE */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move Action Token ahead or behind in a location). *Patent Office: Take 1 New Part, either from the face up parts or from the top of the stack. *Junk Yard: Take up to 2 Junk Parts, either from the 3 face up parts or from the top of the stack. If a face up part is chosen, it is replaced before the second is chosen. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (a motor and at least one fuel tank of the same color), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank but there can be extra fuel tanks. * Colored improvements and fuel require an motor of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. If three people are building the same color of engine it is going to be very hard for all of them to succeed. * Make sure to build often enough because you can hold a maximum of 5 blueprints at a time. Also if you aren't in the first two spots in the workshop you aren't going to get enough actions to finish your car. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. The junk yard also has only the 5 kinds of parts required to finish a vehicle so if you are missing a key part you are much more likely to find it here than blindly drawing from the patent office. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. 8941f1d92c3e211c3ee931038e0e8f7762e2cff5 822 821 2013-05-12T04:36:44Z Jirikki 2655 /* LOCATION ACTIONS */ wikitext text/x-wiki It is 1888, and everywhere you turn, people are talking about the horseless carriage. The players build an automobile prototype by patenting new parts, scavenging in the junk yard, and looking for opportunities to trip up their fellow automotive pioneers, all the while seeking to impress investors that are looking to get into car production. Gear & Piston is a simple, yet engaging 30-60 minute game for 2-6 players, and is [http://www.kickstarter.com/projects/ludicreations/gear-and-piston-board-game now on Kickstarter]. ===QUICK RULES REFERENCE=== ====ROUND SEQUENCE AND PHASES==== *I. Place action tokens. (between I and II, you may discard ONE blueprint) *II. Resolve actions. *III. Refresh Patent Office and Junk Yard. ====LOCATION ACTIONS==== *Back Alley: Choose 1 action from the following. The Action Token placed in the Back Alley cannot be used during the next planning phase. **Black Market (draw 3 New Parts and keep 1), **Espionage (look at one player's blueprints and take one of them), **Union Muscle (move an Action Token ahead in another location). *Patent Office: Take 1 New Part, either from the face up parts or from the top of the stack. *Junk Yard: Take up to 2 Junk Parts, either from the 3 face up parts or from the top of the stack. If a face up part is chosen, it is replaced before the second is chosen. *Workshop: Actions that may be used are Build, Dismantle, and Upgrade. (last player to place a token here is the first player in the next round) ====REQUIREMENTS OF A COMPLETE AUTOMOBILE==== * Each automobile requires at least one engine (a motor and at least one fuel tank of the same color), at least one Gear, at least two Axles and exactly one steering mechanism. * Every motor requires a matching fuel tank but there can be extra fuel tanks. * Colored improvements and fuel require an motor of same color. * Each part on the top row requires a part on the bottom row and vice versa. * All parts must be supported by an axle. Each Axle supports only the five parts surrounding it. ====GAME END==== *There are not enough tiles to refill a location from the refill Stack. OR *One of the players has a complete automobile with at least twelve parts on it. At the end of the game all players must join the two halves of the car and have a complete automobile. Players may need to complete their automobile by adding scrap. Illegal parts must either be covered up or have scrap added to make them legal. Each scrap acts as a single part. In the top row it can be a motor, fuel or steering. In the bottom row it can be an axle or a gear. The smallest complete auto is 6 pieces. Do not keep adding scrap to your vehicle if it would make your car longer than 9 or you risk crashing the game. If your car is that long and is incomplete there is an illegal part that must be covered up. ====SCORING==== * VP according to Investors The first investor cares about the number of each icon. If there are 0 or 1 of range, comfort or power it is worth no points. The first two range are worth 2 or 3, first two power and first two comfort are worth 1, 2 or 3, based on the investor's preference and each additional is always worth +2 VP regardless of the investor's preference. * +1 VP for each part of the same color (red, green, yellow or grey) in the largest continuous section. Each car will have at least 4 grey parts so its likely to be the dominant color. Scrap parts have no color. * -1 VP for each point of Volatility not countered by a wrench icon. ====BEGINNER TIPS==== * Keep an eye on what other players are doing. If three people are building the same color of engine it is going to be very hard for all of them to succeed. * Make sure to build often enough because you can hold a maximum of 5 blueprints at a time. Also if you aren't in the first two spots in the workshop you aren't going to get enough actions to finish your car. * It is advisable to visit the Junk Yard every now and then especially if there are some parts without Volatility for grabs. The junk yard also has only the 5 kinds of parts required to finish a vehicle so if you are missing a key part you are much more likely to find it here than blindly drawing from the patent office. * The game can end surprisingly fast and each Black Market action and blind draw from the Patent Office makes the game end even faster. * Remember that you can upgrade multiple junk parts with one workshop action. 96babae1589a1d46b75e44846654f7ff78eb3124 Keskustelu:Gamehelpgearnpiston 1 120 823 2013-05-12T04:39:59Z Jirikki 2655 Created page with "When using union muscle, are you swapping your token with another or are you sliding all the other tokens down when you move. Is there any reason to move your token down unles..." wikitext text/x-wiki When using union muscle, are you swapping your token with another or are you sliding all the other tokens down when you move. Is there any reason to move your token down unless you are trying to throw the game to another player? f863575cf8f5ead7a658701c4c549caeadd532e0 Gamehelpnorthwestpassage 0 121 826 2013-05-13T16:48:33Z Tirix 1962 Early version wikitext text/x-wiki '''''Looking for the Franklin Expedition''''' ''In 1845, Sir John Franklin led an expedition on behalf of the British Royal Navy to find and explore the last portion of the Northwest Passage. They sailed into unknown waters, and no-one ever heard from them again... Players must venture into these hazardous Arctic waters in order to discover Franklin’s fate and succeed where he failed: by finding the Northwest Passage.'' ---- In the game, prestige is awarded for exploring islands and straits, getting information from Inuits or Cairns about the Franklin Expedition, finding shipwrecks of the expedition, and for being among the first to find their way up to the Northwest Passage and back to Groenland. The player with the most Prestige points at the end of the game is the winner. In case of a tie, the first tied player to have returned to Greenland is the winner. == GAME CYCLE == The game is divided into ten rounds. At the end of each round, the solar disc moves, and part of the board above the sun freezes into ice or melts back to sea. During one round, player will take up to 7 actions, in turn. Actions can be taken either from the ship, using crewmen on the ship, or from the sled, using crewmen on the sled. On the player board, :Ship: 1/2 - Sled: 3/5 means for example that there are two crewmen on the ship and five on the sled, and that one crewman is available (out of two) on the ship, and three crewmen (out of five) are available on the sled. At the end of the round, all used Crewmen move back to «Available». The next turn order is the order in which players used their last crewmen (or passed). This is shown by the turn order markers at the top left of the board. == ACTIONS == === Draw a tile === ''Cost: 1 Crewman'' <Click on the chosen tile at the top of the board.> The player chooses either : * ONE large exploration tiles, chosen from the four available. * ONE small exploration tile, chosen from among the six available. <br/> === Refresh tiles and Draw === ''Cost: 2 Crewmen'' <Click on the "Renew deck" icon.> The player MUST: # Refresh: all large tiles available on the board are replaced with four new tiles which are randomly drawn from the reserve. The replaced tiles are placed back into the reserve. # Draw a tile: he takes either a large or a small Exploration tile and adds it to his personal reserve. <br/> === Place a tile === ''Cost: 1 Crewman'' <Click on the tile from your hand, rotate it or flip it as fit, and click on the white marker on the board.> The player places one tile on the board, which is taken from his personal reserve. He must meet the following conditions: * The tile  must  be placed in line with the black grid on the board. * The tile  must  be placed so that  at  least  one  side (long or large) must be flush with a tile on which the player has a presence (Ship or Sled, depending on which one executes this action). * The land and sea areas of the newly-placed tile must correspond with all land and  sea tiles of adjacent tiles (both orthogonally and diagonally). If, by placing a tile, a player creates a space the size of a small Exploration tile which is surrounded on all sides by tiles, a matching small Exploration tile is taken from the reserve and placed to fill the empty space. An island is complete when the land areas of at least two tiles are completely surrounded by water or the edge of the board. In case a player completes one or several islands, he gets one Cartography token, and Prestige points according to the size of the island. <br/> === Transfer Crew === ''Cost: 1 Crewman'' <br/> === Movement === ''Cost: 1 Crewman'' <Click on the ship or sled, then on its destination.> '''Ship Movement''': The player moves his ship onto one adjacent tile linked to the tile it currently occupies by a sea passage. '''Sled Movement''': The player moves his sled onto one adjacent tile linked to the one it currently occupies by a land passage. The Sled cannot cross the sea unless it is frozen. Multiple Ships and Sleds can be on the same tile. A frozen tile is unreachable for a ship. If a ship, due to a season change, finds itself in frozen waters, the ship is blocked in and will not be able to move until the thaw. A frozen tile is considered to be entirely composed of land, so a Sled can move freely on it. <br/> === Explore a Franklin site or a Strait === ''Cost: 3 Crewmen'' <Click on the ship or sled, then on its destination.> <br/> === Discover an Inuit or a Cairn === ''Cost: 2 Crewmen'' <Click on the ship or sled, then on its destination.> <br/> === Pass === A player can choose to Pass and take no action during his turn, even if he still has available Crewmen. In this case, the player cannot take any more actions during this round. If a player has no available Crewmen, he automatically passes. == END OF THE GAME == The game ends either: at the end of the Action Phase of the tenth round or if all of the Expeditions have returned to Greenland. In addition to the point earned during the game, players receive more Prestige points based on the tokens they picked up along the way. ==== Exploration Points: ==== Point are awarded to the player(s) with the most, second most, and third most Shipwrecks, Straits, and Cartography tokens (see tooltips for scoring). ==== Set Points: ==== Each set is composed of one token of each Discovery type – one Inuit, one Cairn, one Shipwreck, one Strait, and one Cartography token. Each set gives a bonus of 6 prestige points. ==== Abandonment Penalty: ==== Each Sled and/or Ship which did not return to Greenland before the end of the tenth Exploration season is lost. When a Ship or a Sled is lost, all of the crewmen in the corresponding column are lost with it. Every lost Ship and/or Sled gives a penalty of -2 Prestige points for each corresponding lost Crewman. An abandoned ship gives a penalty of -2 Prestige points. ecefa553b528f8319a04ef171f9621b6defd20ce 838 826 2013-05-25T11:12:01Z Tirix 1962 Completed action description wikitext text/x-wiki '''''Looking for the Franklin Expedition''''' ''In 1845, Sir John Franklin led an expedition on behalf of the British Royal Navy to find and explore the last portion of the Northwest Passage. They sailed into unknown waters, and no-one ever heard from them again... Players must venture into these hazardous Arctic waters in order to discover Franklin’s fate and succeed where he failed: by finding the Northwest Passage.'' ---- In the game, prestige is awarded for exploring islands and straits, getting information from Inuits or Cairns about the Franklin Expedition, finding shipwrecks of the expedition, and for being among the first to find their way up to the Northwest Passage and back to Groenland. The player with the most Prestige points at the end of the game is the winner. In case of a tie, the first tied player to have returned to Greenland is the winner. == GAME CYCLE == The game is divided into ten rounds. At the end of each round, the solar disc moves, and part of the board above the sun freezes into ice or melts back to sea. During one round, player will take up to 7 actions, in turn. Actions can be taken either from the ship, using crewmen on the ship, or from the sled, using crewmen on the sled. At the end of the round, all used Crewmen move back to «Available». The next turn order is the order in which players used their last crewmen (or passed). This is shown by the turn order markers at the top left of the board. == ACTIONS == === Draw a tile === ''Cost: 1 Crewman'' <Click on the chosen tile at the top of the board.> <br/> === Refresh tiles and Draw === ''Cost: 2 Crewmen'' <Click on the "Renew deck" icon.> The player MUST: # Refresh: all large tiles available on the board are replaced with four new tiles which are randomly drawn from the reserve. The replaced tiles are placed back into the reserve. # Draw a tile: he takes either a large or a small Exploration tile and adds it to his personal reserve. <br/> === Place a tile === ''Cost: 1 Crewman'' <Click on the tile from your hand, rotate it or flip it as fit, and click on the white marker on the board.> The player places one tile on the board, which is taken from his personal reserve. He must meet the following conditions: * The tile  must  be placed in line with the black grid on the board. * The tile  must  be placed so that  at  least  one  side (long or large) must be flush with a tile on which the player has a presence (Ship or Sled, depending on which one executes this action). * The land and sea areas of the newly-placed tile must correspond with all land and  sea tiles of adjacent tiles (both orthogonally and diagonally). If, by placing a tile, a player creates a space the size of a small Exploration tile which is surrounded on all sides by tiles, a matching small Exploration tile is taken from the reserve and placed to fill the empty space. An island is complete when the land areas of at least two tiles are completely surrounded by water or the edge of the board. In case a player completes one or several islands, he gets one Cartography token, and Prestige points according to the size of the island. <br/> === Transfer Crew === ''Cost: 1 Crewman'' <Click on the back arrows on the player board> Crewmen are moved from/to the ship/sled, thus deploying or taking back the sled into the ship. Taking back crewmen in the ship can only happen if the sled and ship are on the same tile. Both active and resting crewmen can be transferred. <br/> === Movement === ''Cost: 1 Crewman'' <Click on the ship or sled, then on its destination.> '''Ship Movement''': The player moves his ship onto one adjacent tile linked to the tile it currently occupies by a sea passage. '''Sled Movement''': The player moves his sled onto one adjacent tile linked to the one it currently occupies by a land passage. The Sled cannot cross the sea unless it is frozen. Multiple Ships and Sleds can be on the same tile. A frozen tile is unreachable for a ship. If a ship, due to a season change, finds itself in frozen waters, the ship is blocked in and will not be able to move until the thaw. A frozen tile is considered to be entirely composed of land, so a Sled can move freely on it. <br/> === Explore a Franklin site or a Strait === ''Cost: 3 Crewmen'' <Click on the bonus chip.> 1 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3), and additionally, points are awarded at the end of the game for the players having the most bonuses of a given type (see tooltip). <br/> === Discover an Inuit or a Cairn === ''Cost: 2 Crewmen'' <Click on the bonus chip.> 2 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3). <br/> === Pass === A player can choose to Pass and take no action during his turn, even if he still has available Crewmen. In this case, the player cannot take any more actions during this round. If a player has no available Crewmen, he automatically passes. == END OF THE GAME == The game ends either: at the end of the Action Phase of the tenth round or if all of the Expeditions have returned to Greenland. In addition to the point earned during the game, players receive more Prestige points based on the tokens they picked up along the way. ==== Exploration Points: ==== Point are awarded to the player(s) with the most, second most, and third most Shipwrecks, Straits, and Cartography tokens (see tooltips for scoring). ==== Set Points: ==== Each set is composed of one token of each Discovery type – one Inuit, one Cairn, one Shipwreck, one Strait, and one Cartography token. Each set gives a bonus of 6 prestige points. ==== Abandonment Penalty: ==== Each Sled and/or Ship which did not return to Greenland before the end of the tenth Exploration season is lost. When a Ship or a Sled is lost, all of the crewmen in the corresponding column are lost with it. Every lost Ship and/or Sled gives a penalty of -2 Prestige points for each corresponding lost Crewman. An abandoned ship gives a penalty of -2 Prestige points. e58f433c9bf6661de75716fd7b6096bdece65030 Gamehelpstoneage 0 21 827 755 2013-05-16T02:09:26Z Mexiwithacan 3157 /* Place your people on the game board */ Replaced explanation of rule changes for 2- and 3-player games with what's in the official rulebook, verbatim wikitext text/x-wiki == Goal == Get the most victory points at the end of the game. You win some points: * during the game, by acquiring buildings. * at the end of the game, according to civilization cards acquired by you. == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players place their people on the game board. 2. The players use the actions of their placed people. 3. The players feed their people. ===Place your people on the game board=== Players place groups of their people in places, occupying each "ring" by one people (except for the forest place having no rings). ====Special considerations when playing with fewer than 4 people==== With '''3 or 2 players''', only 2 of the 3 places: tool maker, hut, and field may be filled in each round. The third place remains empty. Naturally, the empty place can be different in each round. With '''3 players''': on each of the places: forest, clay pit, quarry, and river only 2 players may place people in each round. With '''2 players''': on each of the places: forest, clay pit, quarry, and river only 1 player may place people in each round. All remaining rules remain unchanged. ===Use the actions of your placed people=== Each player uses all his placed people in any order, and perform the corresponding actions: * '''hunting grounds, forest, clay pit, quarry, and river''': roll 1 dice per people placed and acquire corresponding resources (1 food for each full 2 on hunting grounds, 1 wood for each full 3 on forest, 1 brick for each full 4 on clay pit, 1 stone for each full 5 on quarry, 1 gold for each full 6 on river). * '''field''': increase agriculture level. * '''tool maker''': get one tool point (new tool is added, up to 3 in total, after that these tools are upgraded). * '''hut''': get one additional people. * '''building card''': buy the building with resources, scoring some points. * '''civilization card''': buy the card with a number of resources (of any kind) depending on its position (1 to 4 resources depicted above the card). Then the immediate effect of the card is applied. ===Resources=== hunting grounds, forest, clay pit, quarry, and river: Roll 1 dice per people and take corresponding resources: * 1 food for each full 2 on hunting grounds, * 1 wood for each full 3 on forest, * 1 brick for each full 4 on clay pit, * 1 stone for each full 5 on quarry, * 1 gold for each full 6 on river. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the building and receive points. If the building cost is not fixed (question mark in the corner of the card), you win points for each of the resources used to acquire this building, depending on their values (3 for each wood, 4 for each brick, ...). ===Civilization card details=== Each card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for that top half is not relevant anymore. ===Feed your people=== Each agriculture level automatically give you 1 food. You must provide 1 food per person. You may use any resource to feed your people if there is not enough food. If you don't manage (or choose not) to feed your people by means above, you lose 10 points as a penalty. ===Game end=== The game ends when: * there are no more civilization cards at the beginning of a round * one building stack is empty at the end of a round Final number of points for each player is summed out of * points earned during the game by acquiring buildings, minus food penalties, * multipliers denoted at the bottom halfs of civilization cards collected (like number of different culture cards, number of people, tool level etc). * each resource that a player has on his player board scores 1 point 892f6c221390ffc0b9cef0a00215b3530435ca93 Practical debugging 0 100 828 660 2013-05-19T19:32:34Z Een 3 /* Debugging my PHP game logic (or my view) */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> === Add traces to your code === You can use the following functions in your game to add server side logging: '''self::debug( $message )''' '''self::trace( $message )''' '''self::warn( $message )''' '''self::error( $message )''' To see the logs, you should check the "Current table error log" in the [[Studio_back-office|Studio backoffice]]. This can be useful when you need to follow the flow of your code and not just stop it to see how it goes at some point. Only the error log level will appear in production. This level should be used only for critical problems. Other levels will show only in the development environment and can be used as you see fit. == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. === When I do a move, I got "Move recorded, waiting for update ..." forever === "Move recorded" means that your ajaxcall request has been sent to the server and returned normally. "Waiting for update" means that your client interface is waiting for some notifications from the server that correspond to the move we just did. If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code. === Some player action is triggered randomly when I click somewhere on the game area === You probably used "dojo.connect" on a null object. In this case, dojo.connect associate the event (ex: "onclick") to the whole game area. Most of the time it happens in this situation, when my_object element does not exists: <pre> dojo.connect( $("my_object"), "onclick", this, function() { ... } </pre> To determine if this is the case, place "alert( $("my_object") )" before the dojo.connect to check if the object exists or not. === Javascript does not know how to sum two numbers === Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers. As a result: <pre> var i=1; i += notif.args.increment; // With notif.args.increment='1' alert( i ); // i=11 instead of 2 !! Javascript concatenate 2 strings ! </pre> To solve this, you should use the "toint" function: <pre> var i=1; i += toint( notif.args.increment ); // With notif.args.increment='1' alert( i ); // i=2 :) </pre> === Javascript: do not use substr with negative numbers === To get the last characters of a string, use "slice" instead of "substr" which has a bug on IE: <pre> var three_last_characters = string.substr( -3 ); // Wrong var three_last_characters = string.slice( -3 ); // Correct </pre> 750417058c1bb321927449e3554900012f94de24 829 828 2013-05-19T20:14:01Z Een 3 /* Add traces to your code */ wikitext text/x-wiki This page gives you practical tips to debug your game during the development. Don't hesitate to share with us your difficulties in order we can improve this section. == Tools == To work on BGA Studio, we recommend you to use [www.google.com/chrome Google Chrome] as it's the current fastest browser for BGA platform, and it's available in all OS. Another reason to use Chrome is that it embed all tools you need to work on BGA Studio. You can see them by pressing "F12" or from the menu ("Tools > Development tools"). A good practice is to use a second browser to develop the game, in order to check that your game is working fine on this browser too. To debug with Firefox browser, we advise you to use these 2 extensions: * [https://addons.mozilla.org/firefox/addon/firebug/ Firebug] * [https://addons.mozilla.org/firefox/addon/web-developer/ Web developper] To debug with Internet Explorer, we advise you to use one of the most recent version (ex: IE9). Last versions of Internet Explorer have way better development tools than the previous ones... == General tip for debugging == In general for debugging, think of using the '[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]] state' functionality. It enables you to save the state of your game just before the issue you are investigating, then come back to that point with one click as many times as needed to understand what is going wrong. You can save up to 3 different states. == Debugging my game when it cannot start == If your game don't start because of an error, you are probably in one of these situations: * There is a SQL error in your dbmodel.sql file. * You have a syntax error in your PHP file. * Your PHP "setup" - or any method used during the game initial states - generates an exception. If the error is not explicitly displayed when you click on "Express start", you should check the "Gameserver error log" in the [[Studio_back-office|Studio backoffice]]. == Debugging my PHP game logic (or my view) == Most of the time, debugging PHP is quite easy. Here's what I do when I want to develop/debug some game logic that is triggered by some game action: * At first, I make sure that I can reproduce the needed game situation in one click. To do this, I use the "[[Tools_and_tips_of_BGA_Studio#Save_.26_restore_state|save & restore]]" function. * Another possibility for this is to place a '''die('ok');''' PHP statement right after the PHP I am developing/debugging. This way, I make sure that every request will fail and then nothing will be commited to the database, anyway. * Then, I use '''var_dump''' function to dump PHP variables and check what's wrong, until it works. Example: <pre> // (...my code to debug) var_dump( $my_variable ); die('ok'); // (...my code to debug) </pre> === Add traces to your code === You can use the following functions in your game to add server side logging: '''self::debug( $message );''' // debug level logging '''self::trace( $message );''' // info level logging '''self::warn( $message );''' // warning level logging '''self::error( $message );''' // error level logging To see the logs, you should check the "Current table error log" in the [[Studio_back-office|Studio backoffice]]. This can be useful when you need to follow the flow of your code and not just stop it to see how it goes at some point. Only the error log level will appear in production. This level should be used only for critical problems. Other levels will show only in the development environment and can be used as you see fit. == Debugging my HTML/CSS layout == Situation examples: * why my game element doesn't show up in the interface? * why my CSS property hasn't been applied to this element? * why this game element is displayed at this position? A first useful tip when an element does not show up in the interface is to give it a red background: <pre> #my_element { ... some CSS definitions ... background-color: red; } </pre> This way, you know if the element is not visible because of some of its CSS property or because of anything else. Another tip: sometimes, you change a CSS property with no visible effect on your interface. In that case, add a "display:none" property. If your element does not disappear, the bug probably comes from your CSS selector and not from your CSS property. Using Chrome "Elements" tab (thre first one), you can: * See the CURRENT HTML of your page. Remember that the classical "show page source" is inefficient with BGA as you are modifying the page source with your Javascript code. * Using the "magnifying glass", you can click on any part of your game interface and check it's HTML code and associated CSS styles. * You can even modify directly some CSS property and see if how it looks immediately on the game interface. == Debugging my Javascript game interface logic == Compare to PHP debugging, Javascript debugging can sometimes be painful. Here's are some tips to make your life easier while developing and debugging Javascript: === Do complex things on PHP side === PHP side is more reliable and simpler to debug than Javascript. Then, when you need to perform a complex operation, check first it you can't write it on server side first. The most frequent case is the following: you want to compute possible moves in a game situation. Doing it in Javascript is a nightmare. Then, do it on PHP, and transfer the result to your client interface using the "args" game state property. Note: check Reversi example for this. === Add traces in your code === You can use the following: '''console.log( variable_to_inspect )''' It will give you the object structure of the variable in the Javascript console, without blocking the execution. It's often a good idea to precede this call with a console.log( '### HERE ###' ); to find more easily the appropriate line in the console log. '''alert( variable_to_inspect )''' It will popup what you wish and pause the execution until you click ok. This won't be useful for complex structures, only native types will get plainly displayed. But this is sometimes useful just with messages to make sure which way the execution goes. == Some frequent errors == === when launching the game "Fatal error during creation of database ebd_quoridor_389 Not logged." === Check that you didn't use $g_user or getCurrentPlayerId() in setupNewGame() function or in an "args" function of your state. As these functions are not consequences of a user action, there is no current player defined. As a general rule, you should use getActivePlayerId() and not getCurrentPlayerId(). See the [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine presentation on the game state machine] for more information. === when refreshing the web page, the interface remains on "Application loading..." === In this case, you probably have a syntax error in your Javascript code, and the interface refuses to load. To find this error, check if there is an error message in the Javascript console (F12). If there is really nothing on the log, it's probably that the system was unable to load your Javascript because of an syntax error that affect the structure of the Javascript file, typically a missing "}" or a missing "," after a method definition. === When I do a move, I got "Move recorded, waiting for update ..." forever === "Move recorded" means that your ajaxcall request has been sent to the server and returned normally. "Waiting for update" means that your client interface is waiting for some notifications from the server that correspond to the move we just did. If this message stays forever, it is probably that your PHP code does not send any notification when the move happens, which is abnormal. To fix this: add a notifyAllPlayers or a notifyPlayer call in your PHP code. === Some player action is triggered randomly when I click somewhere on the game area === You probably used "dojo.connect" on a null object. In this case, dojo.connect associate the event (ex: "onclick") to the whole game area. Most of the time it happens in this situation, when my_object element does not exists: <pre> dojo.connect( $("my_object"), "onclick", this, function() { ... } </pre> To determine if this is the case, place "alert( $("my_object") )" before the dojo.connect to check if the object exists or not. === Javascript does not know how to sum two numbers === Be careful when you manipulate integers returned by notifications: most of the time, Javascript considers they are Strings and not Integers. As a result: <pre> var i=1; i += notif.args.increment; // With notif.args.increment='1' alert( i ); // i=11 instead of 2 !! Javascript concatenate 2 strings ! </pre> To solve this, you should use the "toint" function: <pre> var i=1; i += toint( notif.args.increment ); // With notif.args.increment='1' alert( i ); // i=2 :) </pre> === Javascript: do not use substr with negative numbers === To get the last characters of a string, use "slice" instead of "substr" which has a bug on IE: <pre> var three_last_characters = string.substr( -3 ); // Wrong var three_last_characters = string.slice( -3 ); // Correct </pre> ac98a8a9b9102f90c29db809630e8f484835d624 Gamehelphearts 0 52 830 726 2013-05-20T13:03:03Z Sourisdudesert 1 wikitext text/x-wiki Hearts is a card game for 4 players, every man for himself. It uses the standard 52-card pack. '''Rank''' A (high) to 2 (low). '''Setup''' The entire deck is dealt, giving you a hand of 13 cards. After looking at your hand, choose three cards to pass to another player. The passing rotation is: (1st hand) to the player on your left, (2nd hand) to the player across the table, (3rd hand) to the player on your right, (4th hand) no passing. The rotation repeats until the game ends. '''Tricks''' After passing cards, the player holding the 2 of clubs leads the first trick. Each player must follow suit if possible. If you have no cards of the suit led, you may play a card of any suit. Exception: You may not play a heart or the Queen of Spades on the first trick, even if you have no clubs. The highest card of the suit led wins the trick. The winner of a trick starts the next trick. Hearts may not be led until a heart has been played (this is called "breaking" hearts). The Queen of Spades may be lead at any time. [NB: In traditional Hearts, playing the Queen of Spades also breaks hearts.] There is no trump suit. '''Scoring''' At the end of each hand, each heart taken by a player counts -1, and the Queen of Spades counts -13. If one player has taken all 13 hearts ''and'' the Queen of Spades (this is known as "shooting the moon", or "slam"), that player scores 0 and all other players score -26. Warning other players about an attempt to shoot the moon is team play, and a breach of Hearts etiquette. When one or more players reach zero, the game ends. The player with the highest score wins. '''Variants''' Normal game is 100 points. Quick game is 75 points. 30e89bee40751176339d12f280b2f8d9cb48a3ca Studio FAQ 0 53 831 779 2013-05-20T13:04:57Z Sourisdudesert 1 /* I added some game options / some game statistics, but they don't show? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I can't access the Studio back-office, I get a 'Not authorized' error message? == You should first connect to the Studio website (with any of your <developer name><number> accounts). As the authentication is shared with the back-office, then you will be able to access it. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == When you modify these 2 files, you need to deploy the update from your BGA backoffice page. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. c45f3903b02b0d17a05cfa31d8138ad13de036b2 Game interface stylesheet: yourgamename.css 0 96 832 683 2013-05-20T13:14:58Z Sourisdudesert 1 wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('../../img/emptygame/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('../../img/emptygame/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. Important: ALL the CSS directives for your game must be included in this CSS file. You can't create additional CSS files and import them. == Warning: using Z-index == You may use z-index CSS property in your game interface, but you should pay attention to the following: BGA dialogs are displayed with a z-index of 950. If you want to use z-index safely, you should use value '''lower than 900'''. About z-index: don't forget that if you are using a z-index, your element will be displayed above all elements that do not have a z-index. So it's no use to have big z-index values: 1 is enough most of the time :) == spectatorMode == When a spectator (= a player that is not part of the game) is viewing a game, the BGA framework add the CSS class "spectatorMode" to the wrapping HTML tag of your game. This way, if you want to apply a special style to some elements of your game for spectators, you can do this in your CSS: <pre> .spectatorMode #your_element_id { /* your special style */ } </pre> The most common usage of this is to hide some elements to spectators. For example, to hide "my hand" elements: <pre> .spectatorMode #my_hand { display: none; } </pre> 1a49ab7082c77d4a53a5d5ceb8de2e5022b7c059 Game interface logic: yourgamename.js 0 88 833 728 2013-05-20T19:18:50Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "AnimateProperty". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 8cd1006ed8d671520c0a8a0a1280dad598241c58 834 833 2013-05-20T19:23:38Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> dd34a1edd72c4ec2e3ea05e293be06a58723bd1c Main game logic: yourgamename.game.php 0 86 835 758 2013-05-21T16:24:52Z Tilalilalou 2792 /* Zombie mode */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaSystemVisibleException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaSystemVisibleException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. d1eb49ba1db74c83e8b9894d730fff16f76acca5 837 835 2013-05-22T09:01:51Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your Constructor method like this: <pre> $this->tie_breaker_description = self::_("Describe here your tie breaker formula"); </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaVisibleSystemException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaVisibleSystemException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. 558ac70cea14f3ea2d2f6a5577f79daacfd7ca0e Gamehelpyatzy 0 122 840 2013-05-28T02:42:36Z Ryanmc 3227 Created page with "Be aware the rules linked from Wikipedia are NOT the rules implemented. The rules implemented vary a great deal from the real rules, and make a dive game even more luck based...." wikitext text/x-wiki Be aware the rules linked from Wikipedia are NOT the rules implemented. The rules implemented vary a great deal from the real rules, and make a dive game even more luck based. The rules for 3 of a kind, 4 of a kind, small straight, and large straight are all different. b651128ff941751e18c98776abb1b7d9f81e2da6 841 840 2013-05-28T02:44:47Z Ryanmc 3227 wikitext text/x-wiki Be aware the rules linked from Wikipedia are NOT the rules implemented. The rules implemented vary a great deal from the real rules, and make a dive game even more luck based. The rules for 3 of a kind, 4 of a kind, small straight, and large straight are all different. In this implementation 3 and 4 of a kind only counts the dice used in the pairing, not the other dice. The small straight is completely different, rather than being a 4 long straight it is explicitly a straight of 1 - 2 - 3 - 4 - 5. The large straight is only a straight of 2 - 3 - 4 - 5 - 6. This makes the odds identical for both straights, but has the large still worth more points. 5a33aea3e935344464ef886108e982d0a91b4b81 842 841 2013-05-28T02:45:38Z Ryanmc 3227 wikitext text/x-wiki Be aware the rules linked from Wikipedia are NOT the rules implemented. The rules implemented vary a great deal from the real rules, and make the game much more random and luck-based. The rules for 3 of a kind, 4 of a kind, small straight, and large straight are all different. In this implementation 3 and 4 of a kind only counts the dice used in the pairing, not the other dice. The small straight is completely different, rather than being a 4 long straight it is explicitly a straight of 1 - 2 - 3 - 4 - 5. The large straight is only a straight of 2 - 3 - 4 - 5 - 6. This makes the odds identical for both straights, but has the large still worth more points. 24726458c99dbf8c7b6939aa0d6508a2b49bc5de Gamehelpraceforthegalaxy 0 41 843 193 2013-05-28T12:28:48Z Sternenfee83 3127 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. 7adcc6f45854f68b93b0161eeb54cf8e91ce7e8d Gamehelpseasons 0 43 844 784 2013-05-28T12:34:12Z Sternenfee83 3127 wikitext text/x-wiki Season is a game of generating points (crystals) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: ''Star'' - increase the maximum card player can summon (max 15) ''Elements'' - Gain an energy of the element shown (water, earth, air, fire) ''Numbers'' - Gain the number of Crystals as indicated by the number ''Square card'' - Draw a card ''Dice with frames surrounding'' - Allow user to transmute energy into crystal (depend on which part of the game is at) ''Dots'' - Indicate how fast the marker progresses through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There are 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each played card and the crystal owned and substracting 5 points for each power card remaining in hand. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. == Difficulty levels: == '''Apprentice wizard level''' In order to make the game easier to learn, there are “pre-constructed” sets of nine power cards. Instead of taking step 1 of the first game phase, each player gets one of these sets. '''Magician level''' Only the power cards numbered 1 to 30 are in play. These cards have easy to grasp effects and will allow you to slowly discover the world of Seasons. '''Archmage level''' All 50 kinds of power cards are in play. The cards numbered 31 through 50 have more complex effects than the basic cards, but will allow you to extend the fun of playing by discovering new effects and combinations. 4601b4113946cbb3689946db68fb320b36bf1241 Moderationpolicy 0 123 845 2013-05-30T09:10:34Z Sourisdudesert 1 Created page with "fff" wikitext text/x-wiki fff f6949a8c7d5b90b4a698660bbfb9431503fbb995 846 845 2013-05-30T09:16:56Z Sourisdudesert 1 wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. 3ee8f8ae18afb549b01482b3d46240a70c77286b 847 846 2013-05-30T09:18:38Z Sourisdudesert 1 wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- | ! scope="col" | Titre col. A ! scope="col" | Titre col. B ! scope="col" | Titre col. C |- ! scope="row" | Titre ligne 1 | Donnée 1A | Donnée 1B | Donnée 1C |- ! scope="row" | Titre ligne 2 | Donnée 2A | Donnée 2B | Donnée 2C |- ! scope="row" | Titre ligne 3 | Donnée 3A | Donnée 3B | Donnée 3C |} 6ae857612b37650f114fa721c3a5a5abdb5018d1 848 847 2013-05-30T09:19:59Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- | ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! scope="row" | Titre ligne 1 | Donnée 1A | Donnée 1B | Donnée 1C |- ! scope="row" | Titre ligne 2 | Donnée 2A | Donnée 2B | Donnée 2C |- ! scope="row" | Titre ligne 3 | Donnée 3A | Donnée 3B | Donnée 3C |} 4991abca78b6669759c1e7fe1371aed63e559cf9 849 848 2013-05-30T09:28:47Z Sourisdudesert 1 /* General rules */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- | ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! scope="row" | Titre ligne 1 | Donnée 1A | Donnée 1B | Donnée 1C |- ! scope="row" | Titre ligne 2 | Donnée 2A | Donnée 2B | Donnée 2C |- ! scope="row" | Titre ligne 3 | Donnée 3A | Donnée 3B | Donnée 3C |} 5a0d60aab93a8b90f81fc71a8328364d7efa13fe 850 849 2013-05-30T09:29:32Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- | ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Donnée 1A | Donnée 1A | Donnée 1B | Donnée 1C |- ! Donnée 1A | Donnée 2A | Donnée 2B | Donnée 2C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |} 4d8038cf38fb161ac98f3a025e4eb5033f147b0a 851 850 2013-05-30T09:38:11Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |} 87a132a31b14d541809b5f0cd1b8aa4789750a37 Moderationpolicy 0 123 852 851 2013-05-30T09:42:40Z Sourisdudesert 1 /* General rules */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |} 65b38ed4d46b022c0ccce6a65ce099891e091650 853 852 2013-05-30T09:51:19Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 0ac9b3ec502c18ff1f45e58dc2c3742c587bf1ae 854 853 2013-05-30T09:59:42Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 934bd58f5f40ff95414519ec5ada22b98e0f1d14 855 854 2013-05-30T10:05:16Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 1b3b4318420c869b396c0be9c3512ff35afe50c9 856 855 2013-05-30T10:07:32Z Sourisdudesert 1 /* Player profile influence */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 9043dcf975efceb574d1aa72e2526aa26923bc53 857 856 2013-05-30T10:14:21Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 7d97e2d01a1118a2f9a09532db522f4f273317c3 858 857 2013-05-30T10:20:40Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset will be done. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 652fc40f9ec94d9d3fb458ab0f9ae63380d39ece 859 858 2013-05-30T10:21:14Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 1868411fd8423695a638e416421d3aa1ed75f6b7 860 859 2013-05-30T10:25:54Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} 22cd9d141acbe98da1c8d1bc8baf7fc89b6f1515 861 860 2013-05-30T10:34:18Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- ! Donnée 1A | Donnée 3A | Donnée 3B | Donnée 3C |- |} e68acba7624471111338d95c073aa8a7a69a368b 862 861 2013-05-30T10:39:38Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: _ the player has been reported several time for the same type of case. _ the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. _ on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Tranlation stealing / vandalism | The player is writing inapropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their alloted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 6a2e94ac4dd960b596e6862a25383fe59427855e 863 862 2013-05-30T18:44:30Z Een 3 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several time for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Tranlation stealing / vandalism | The player is writing inapropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their alloted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 93f81aa01f96a0d3c2096e23f0690a7d548f371b 864 863 2013-05-30T18:44:44Z Een 3 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators gives more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Tranlation stealing / vandalism | The player is writing inapropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their alloted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 1939137892ffed5f2613689baae03d0945413f78 865 864 2013-05-30T18:52:15Z Een 3 /* Player profile influence */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games player. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Tranlation stealing / vandalism | The player is writing inapropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their alloted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 68d4eabd3846dfb8f22862ab8bbdaba0c8329ab6 866 865 2013-05-30T18:52:46Z Een 3 /* Player profile influence */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games played. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one notice the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it have to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong voluntee to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. This is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP adress, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Tranlation stealing / vandalism | The player is writing inapropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their alloted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 564f5ed8ba30da2f5125e2ed47d86bfa6b0d0108 867 866 2013-05-30T19:13:08Z Een 3 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games played. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: porn, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (porn, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one noticed the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it has to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong intent to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. It is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP address, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Translation stealing / vandalism | The player is writing inappropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their allotted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} d6bd98861dfb4fa6b1ad8b145d252c8f61df7843 Gamehelpreversi 0 124 868 2013-05-31T20:04:56Z Elisha 2997 Created page with "[English Rules: Link][http://en.wikipedia.org/wiki/Reversi]" wikitext text/x-wiki [English Rules: Link][http://en.wikipedia.org/wiki/Reversi] 245acab3bf59561ee3167722bfe3b03c5fb3d769 Gamehelpstoneage 0 21 869 827 2013-06-01T05:13:44Z Crazyamazing 3028 Minor rules clarifications wikitext text/x-wiki == Goal == Gain the most Victory Points by the end of the game. There are two ways to gain points: * Acquiring buildings during the game for either a fixed or variable amount * Acquiring Civilization cards during the game that give extra points at the end of the game == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players take turns placing their people on the game board. 2. The players use the actions of their placed people all at once in turn order. 3. The players feed their people. ===Phase 1:=== ===Place People on the Board=== In turn order, one area at a time, players place one or more of their people in the different areas of the board. Each "ring" in each area of the board can only be occupied by one person. The exception to this rule is the hunting ground which can be occupied by as many people of any color every turn. ====Special Considerations When Playing With Fewer Than 4 Players==== When playing with '''2 or 3 players''': Of the non-resource areas (Tool Maker, Hut and Field) only two may be occupied per turn. The third place remains empty until the next round when With '''3 players''': Of the resource areas (Forest, Clay Pit, Quarry and River) only 2 players may place people in each round, even if there are additional spaces for additional people. With '''2 players''': Of the resource areas (Forest, Clay Pit, Quarry and River) only 1 player may place people in each round, even if there are additional spaces for additional people. All remaining rules remain unchanged when playing with fewer than 4 players. ===Phase 2:=== ===Use the Actions of Your Placed People=== In turn order each player uses all of his placed people in any order. During this phase, the following actions may be used: * '''Hunting Grounds (Food), Forest (Wood), Clay Pit (Brick), Quarry (Stone), and River (Gold)''': Roll 1 dice per person placed and acquire resources according to the number rolled (1 Food for each full 2 on Hunting Grounds, 1 Wood for each full 3 on Forest, 1 Brick for each full 4 on Clay Pit, 1 Stone for each full 5 on Quarry, 1 Gold for each full 6 on River). * '''Field''': Placing a person in the Field increases your Agriculture Level. Each turn during the Feeding Phase, your Agriculture Level is subtracted from the amount of people you have. A higher agriculture level allows you to more easily sustain your population. * '''Tool Maker''': Placing a person on the Tool Maker adds one new Tool to your player board. You only have 3 tools on your player board, and each subsequent person placed on the Tool Maker upgrades one of your tools to the next level. * '''Hut''': Place two People on the Hut to gain an extra Person to place (and feed) for the rest of the game. * '''Building Card''': Buy the building with the resources shown on the Building Card, scoring the point value shown in the top-left corner (A question mark in the top-left corner is explained below). * '''Civilization Card''': Buy the card with a number of resources (of any kind) depending on its position in the Civilization Card Track (1 to 4 resources depicted above the card). The immediate effect of the card is applied when purchase. The bottom half of Civilization Card is used for scoring at the end of the game. ===Resources=== Hunting Grounds, Forest, Clay Pit, Quarry, and River: Roll 1 dice per Person placed in that area and take the corresponding resources: * 1 Food for each full 2 on Hunting Grounds, * 1 Wood for each full 3 on Forest, * 1 Brick for each full 4 on Clay Pit, * 1 Stone for each full 5 on Quarry, * 1 Gold for each full 6 on River. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the Building and receive the Point Value listed in the top-left corner of the card. ''If the building cost is Not Fixed'': If there is a question mark in the corner of the card, you win points for each of the resources used to acquire this building. The values of each resource correspond to the numbers it requires when people are placed on them: * Each Wood is worth 3 Points, * Each Brick is worth 4 Points, * Each Stone is worth 5 Points, * Each Gold is worth 6 Points. ===Civilization Card Details=== Each Civilization Card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for the top half is no longer relevant. ===Phase 3:=== ===Feed Your People=== Each Agriculture Level automatically counts as 1 Food during this phase. You must provide 1 Food per Person. In the event that you don't have enough food, you may use any resource to feed your people. If you don't manage (or choose not) to feed your people by the means above, you lose all of your food and 10 points as a penalty. ===Game End=== The game ends when either: * One Building Stack is empty at the end of a round or * There are no more Civilization Cards at the beginning of a round The final number of points for each player is decided by the conditions below: * Points earned during the game by acquiring Buildings, minus Food Penalties, * Multipliers denoted at the bottom halves of Civilization Cards collected, * Each Resource that a Player has leftover on his Player Board Scores 1 Point f71bdd22c0cc48908fc9fedbe676b0fcd488239f 870 869 2013-06-01T05:18:15Z Crazyamazing 3028 Header Editing wikitext text/x-wiki == Goal == Gain the most Victory Points by the end of the game. There are two ways to gain points: * Acquiring buildings during the game for either a fixed or variable amount * Acquiring Civilization cards during the game that give extra points at the end of the game == Rules summary == Each round is divided into 3 phases, which are executed in the order described: 1. The players take turns placing their people on the game board. 2. The players use the actions of their placed people all at once in turn order. 3. The players feed their people. == Phase 1: == ===Place People on the Board=== In turn order, one area at a time, players place one or more of their people in the different areas of the board. Each "ring" in each area of the board can only be occupied by one person. The exception to this rule is the hunting ground which can be occupied by as many people of any color every turn. ====Special Considerations When Playing With Fewer Than 4 Players==== When playing with '''2 or 3 players''': Of the non-resource areas (Tool Maker, Hut and Field) only two may be occupied per turn. The third place remains empty until the next round when With '''3 players''': Of the resource areas (Forest, Clay Pit, Quarry and River) only 2 players may place people in each round, even if there are additional spaces for additional people. With '''2 players''': Of the resource areas (Forest, Clay Pit, Quarry and River) only 1 player may place people in each round, even if there are additional spaces for additional people. All remaining rules remain unchanged when playing with fewer than 4 players. == Phase 2: == ===Use the Actions of Your Placed People=== In turn order each player uses all of his placed people in any order. During this phase, the following actions may be used: * '''Hunting Grounds (Food), Forest (Wood), Clay Pit (Brick), Quarry (Stone), and River (Gold)''': Roll 1 dice per person placed and acquire resources according to the number rolled (1 Food for each full 2 on Hunting Grounds, 1 Wood for each full 3 on Forest, 1 Brick for each full 4 on Clay Pit, 1 Stone for each full 5 on Quarry, 1 Gold for each full 6 on River). * '''Field''': Placing a person in the Field increases your Agriculture Level. Each turn during the Feeding Phase, your Agriculture Level is subtracted from the amount of people you have. A higher agriculture level allows you to more easily sustain your population. * '''Tool Maker''': Placing a person on the Tool Maker adds one new Tool to your player board. You only have 3 tools on your player board, and each subsequent person placed on the Tool Maker upgrades one of your tools to the next level. * '''Hut''': Place two People on the Hut to gain an extra Person to place (and feed) for the rest of the game. * '''Building Card''': Buy the building with the resources shown on the Building Card, scoring the point value shown in the top-left corner (A question mark in the top-left corner is explained below). * '''Civilization Card''': Buy the card with a number of resources (of any kind) depending on its position in the Civilization Card Track (1 to 4 resources depicted above the card). The immediate effect of the card is applied when purchase. The bottom half of Civilization Card is used for scoring at the end of the game. ===Resources=== Hunting Grounds, Forest, Clay Pit, Quarry, and River: Roll 1 dice per Person placed in that area and take the corresponding resources: * 1 Food for each full 2 on Hunting Grounds, * 1 Wood for each full 3 on Forest, * 1 Brick for each full 4 on Clay Pit, * 1 Stone for each full 5 on Quarry, * 1 Gold for each full 6 on River. You may use tools to increase the dice result and take more resources. ===Buildings=== Pay the building cost to get the Building and receive the Point Value listed in the top-left corner of the card. ''If the building cost is Not Fixed'': If there is a question mark in the corner of the card, you win points for each of the resources used to acquire this building. The values of each resource correspond to the numbers it requires when people are placed on them: * Each Wood is worth 3 Points, * Each Brick is worth 4 Points, * Each Stone is worth 5 Points, * Each Gold is worth 6 Points. ===Civilization Card Details=== Each Civilization Card acquired brings an immediate advantage (depicted on the top half of the card) and some multiplier for final scoring (depicted on the bottom half of the card). Place the mouse cursor on each card to see details in the tooltip. Note that at the list of your acquired civilization cards you will see only the bottom half of each card, for the top half is no longer relevant. == Phase 3: == ===Feed Your People=== Each Agriculture Level automatically counts as 1 Food during this phase. You must provide 1 Food per Person. In the event that you don't have enough food, you may use any resource to feed your people. If you don't manage (or choose not) to feed your people by the means above, you lose all of your food and 10 points as a penalty. ===Game End=== The game ends when either: * One Building Stack is empty at the end of a round or * There are no more Civilization Cards at the beginning of a round The final number of points for each player is decided by the conditions below: * Points earned during the game by acquiring Buildings, minus Food Penalties, * Multipliers denoted at the bottom halves of Civilization Cards collected, * Each Resource that a Player has leftover on his Player Board Scores 1 Point bf846958420a3a6029ddf8def947fe382ed09d03 Gamehelpniagara 0 119 871 839 2013-06-01T16:45:56Z Pejsek2 3255 better-looking gem table wikitext text/x-wiki Players paddle canoes along the Niagara river to collect gems of five different colors and bring them safely back to shore without being swept over the waterfall. To win, a player must return either: * five different colored gems * four gems of the same color * seven gems of any color The tie break is the value of the gems - the gems closer to the waterfall have a higher value. In order the gems are: {| class="wikitable" ! align="left"| Gem ! Color ! Value |- |Amethyst |Violet |1 |- |Diamond |Clear |2 |- |Amber |Yellow |3 |- |- |Sapphire |Blue |4 |- |- |Ruby |Pink |5 |} Players have a set of 7 paddle cards which they play to determine the speed of their canoes and the speed of the river. All players must select the card they will play before the players then move their canoes in turn order. Once a card has been played it is not available to play again until all 7 cards have been played. After all the players have moved their canoes the river moves causing all the canoes on the river to be swept downstream. The river moves the same number of places as the lowest value paddle card played plus or minus a modifier depending on the weather. At the beginning of the game the weather is set to 0 so there is no modification but each player also has a cloud card as part of their set of paddle cards that allows them to adjust the weather and hence make the river speed up or slow down. The number on the paddle card determines the number of spaces that the canoe moves - it costs 2 paddle points to pick up (or offload) a gem. Thus if a player chose their 5 card they could move their canoe 5 spaces *or* they could move their canoe 3 spaces and pick up a gem (or they could offload a gem and move their canoe 3 spaces). Both canoes may be moved with the same paddle card - both canoes are moved the same amount, the paddles points are not split between the canoes. When both canoes are out of the water only one can be launched in that turn. If both canoes are in the water they must both be moved using the full value of the paddle points - a player cannot chose not to move one canoe and must always use the full value of the paddle points. However if one of the canoes is out of the water a player may choose not to launch it. Canoes may only move upstream or downstream in a single turn - it is not permitted to change direction in the middle of the turn. However one canoe could move upstream and the second one downstream. Players may steal gems from other canoes, but only after moving upstream and only if they finish on the same space as the other player (and only if they have an empty canoes and hence space to load the gem). Turn order is therefore crucial for stealing. 12cc267ba3dbd74e0d23e887ffa80fc1d0f05f81 Gamehelpnorthwestpassage 0 121 872 838 2013-06-02T04:00:04Z Tirix 1962 Mentioned additional crewman cost for addititional actions wikitext text/x-wiki '''''Looking for the Franklin Expedition''''' ''In 1845, Sir John Franklin led an expedition on behalf of the British Royal Navy to find and explore the last portion of the Northwest Passage. They sailed into unknown waters, and no-one ever heard from them again... Players must venture into these hazardous Arctic waters in order to discover Franklin’s fate and succeed where he failed: by finding the Northwest Passage.'' ---- In the game, prestige is awarded for exploring islands and straits, getting information from Inuits or Cairns about the Franklin Expedition, finding shipwrecks of the expedition, and for being among the first to find their way up to the Northwest Passage and back to Groenland. The player with the most Prestige points at the end of the game is the winner. In case of a tie, the first tied player to have returned to Greenland is the winner. == GAME CYCLE == The game is divided into ten rounds. At the end of each round, the solar disc moves, and part of the board above the sun freezes into ice or melts back to sea. During one round, player will take up to 7 actions, in turn. Actions can be taken either from the ship, using crewmen on the ship, or from the sled, using crewmen on the sled. During one turn, one player can play one or more actions. However, '''all additional actions (beyond the first one) will cost one more crewman than normal'''. In order to stop playing more actions during one turn, click the blincking [End Turn] at the top of the screen. At the end of the round, all used Crewmen move back to «Available». The next turn order is the order in which players used their last crewmen (or passed). This is shown by the turn order markers at the top left of the board. == ACTIONS == === Draw a tile === ''Cost: 1 Crewman'' <Click on the chosen tile at the top of the board.> <br/> === Refresh tiles and Draw === ''Cost: 2 Crewmen'' <Click on the "Renew deck" icon.> The player MUST: # Refresh: all large tiles available on the board are replaced with four new tiles which are randomly drawn from the reserve. The replaced tiles are placed back into the reserve. # Draw a tile: he takes either a large or a small Exploration tile and adds it to his personal reserve. <br/> === Place a tile === ''Cost: 1 Crewman'' <Click on the tile from your hand, rotate it or flip it as fit, and click on the white marker on the board.> The player places one tile on the board, which is taken from his personal reserve. He must meet the following conditions: * The tile  must  be placed in line with the black grid on the board. * The tile  must  be placed so that  at  least  one  side (long or large) must be flush with a tile on which the player has a presence (Ship or Sled, depending on which one executes this action). * The land and sea areas of the newly-placed tile must correspond with all land and  sea tiles of adjacent tiles (both orthogonally and diagonally). If, by placing a tile, a player creates a space the size of a small Exploration tile which is surrounded on all sides by tiles, a matching small Exploration tile is taken from the reserve and placed to fill the empty space. An island is complete when the land areas of at least two tiles are completely surrounded by water or the edge of the board. In case a player completes one or several islands, he gets one Cartography token, and Prestige points according to the size of the island. <br/> === Transfer Crew === ''Cost: 1 Crewman'' <Click on the back arrows on the player board> Crewmen are moved from/to the ship/sled, thus deploying or taking back the sled into the ship. Taking back crewmen in the ship can only happen if the sled and ship are on the same tile. Both active and resting crewmen can be transferred. <br/> === Movement === ''Cost: 1 Crewman'' <Click on the ship or sled, then on its destination.> '''Ship Movement''': The player moves his ship onto one adjacent tile linked to the tile it currently occupies by a sea passage. '''Sled Movement''': The player moves his sled onto one adjacent tile linked to the one it currently occupies by a land passage. The Sled cannot cross the sea unless it is frozen. Multiple Ships and Sleds can be on the same tile. A frozen tile is unreachable for a ship. If a ship, due to a season change, finds itself in frozen waters, the ship is blocked in and will not be able to move until the thaw. A frozen tile is considered to be entirely composed of land, so a Sled can move freely on it. <br/> === Explore a Franklin site or a Strait === ''Cost: 3 Crewmen'' <Click on the bonus chip.> 1 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3), and additionally, points are awarded at the end of the game for the players having the most bonuses of a given type (see tooltip). <br/> === Discover an Inuit or a Cairn === ''Cost: 2 Crewmen'' <Click on the bonus chip.> 2 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3). <br/> === Pass === A player can choose to Pass and take no action during his turn, even if he still has available Crewmen. In this case, the player cannot take any more actions during this round. If a player has no available Crewmen, he automatically passes. == END OF THE GAME == The game ends either: at the end of the Action Phase of the tenth round or if all of the Expeditions have returned to Greenland. In addition to the point earned during the game, players receive more Prestige points based on the tokens they picked up along the way. ==== Exploration Points: ==== Point are awarded to the player(s) with the most, second most, and third most Shipwrecks, Straits, and Cartography tokens (see tooltips for scoring). ==== Set Points: ==== Each set is composed of one token of each Discovery type – one Inuit, one Cairn, one Shipwreck, one Strait, and one Cartography token. Each set gives a bonus of 6 prestige points. ==== Abandonment Penalty: ==== Each Sled and/or Ship which did not return to Greenland before the end of the tenth Exploration season is lost. When a Ship or a Sled is lost, all of the crewmen in the corresponding column are lost with it. Every lost Ship and/or Sled gives a penalty of -2 Prestige points for each corresponding lost Crewman. An abandoned ship gives a penalty of -2 Prestige points. 52900610215c1c81ffdb67435634a2f1d3cc2098 873 872 2013-06-02T10:38:27Z Tirix 1962 wikitext text/x-wiki '''''Looking for the Franklin Expedition''''' ''In 1845, Sir John Franklin led an expedition on behalf of the British Royal Navy to find and explore the last portion of the Northwest Passage. They sailed into unknown waters, and no-one ever heard from them again... Players must venture into these hazardous Arctic waters in order to discover Franklin’s fate and succeed where he failed: by finding the Northwest Passage.'' ---- In the game, prestige is awarded for exploring islands and straits, getting information from Inuits or Cairns about the Franklin Expedition, finding shipwrecks of the expedition, and for being among the first to find their way up to the Northwest Passage and back to Groenland. The player with the most Prestige points at the end of the game is the winner. In case of a tie, the first tied player to have returned to Greenland is the winner. == GAME CYCLE == The game is divided into ten rounds. At the end of each round, the solar disc moves, and part of the board above the sun freezes into ice or melts back to sea. During one round, player will take up to 7 actions, in turn. Actions can be taken either from the ship, using crewmen on the ship, or from the sled, using crewmen on the sled. During one turn, one player can play one or more actions. However, '''all additional actions (beyond the first one) will cost one more crewman than normal'''. In order to stop playing more actions during one turn, click the blinking [End Turn] at the top of the screen. At the end of the round, all used Crewmen move back to «Available». The next turn order is the order in which players used their last crewmen (or passed). This is shown by the turn order markers at the top left of the board. == ACTIONS == === Draw a tile === ''Cost: 1 Crewman'' <Click on the chosen tile at the top of the board.> <br/> === Refresh tiles and Draw === ''Cost: 2 Crewmen'' <Click on the "Renew deck" icon.> The player MUST: # Refresh: all large tiles available on the board are replaced with four new tiles which are randomly drawn from the reserve. The replaced tiles are placed back into the reserve. # Draw a tile: he takes either a large or a small Exploration tile and adds it to his personal reserve. <br/> === Place a tile === ''Cost: 1 Crewman'' <Click on the tile from your hand, rotate it or flip it as fit, and click on the white marker on the board.> The player places one tile on the board, which is taken from his personal reserve. He must meet the following conditions: * The tile  must  be placed in line with the black grid on the board. * The tile  must  be placed so that  at  least  one  side (long or large) must be flush with a tile on which the player has a presence (Ship or Sled, depending on which one executes this action). * The land and sea areas of the newly-placed tile must correspond with all land and  sea tiles of adjacent tiles (both orthogonally and diagonally). If, by placing a tile, a player creates a space the size of a small Exploration tile which is surrounded on all sides by tiles, a matching small Exploration tile is taken from the reserve and placed to fill the empty space. An island is complete when the land areas of at least two tiles are completely surrounded by water or the edge of the board. In case a player completes one or several islands, he gets one Cartography token, and Prestige points according to the size of the island. <br/> === Transfer Crew === ''Cost: 1 Crewman'' <Click on the back arrows on the player board> Crewmen are moved from/to the ship/sled, thus deploying or taking back the sled into the ship. Taking back crewmen in the ship can only happen if the sled and ship are on the same tile. Both active and resting crewmen can be transferred. <br/> === Movement === ''Cost: 1 Crewman'' <Click on the ship or sled, then on its destination.> '''Ship Movement''': The player moves his ship onto one adjacent tile linked to the tile it currently occupies by a sea passage. '''Sled Movement''': The player moves his sled onto one adjacent tile linked to the one it currently occupies by a land passage. The Sled cannot cross the sea unless it is frozen. Multiple Ships and Sleds can be on the same tile. A frozen tile is unreachable for a ship. If a ship, due to a season change, finds itself in frozen waters, the ship is blocked in and will not be able to move until the thaw. A frozen tile is considered to be entirely composed of land, so a Sled can move freely on it. <br/> === Explore a Franklin site or a Strait === ''Cost: 3 Crewmen'' <Click on the bonus chip.> 1 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3), and additionally, points are awarded at the end of the game for the players having the most bonuses of a given type (see tooltip). <br/> === Discover an Inuit or a Cairn === ''Cost: 2 Crewmen'' <Click on the bonus chip.> 2 prestige point is awarded for the discovery multiplied by the zone multiplier (x1 / x2 / x3). <br/> === Pass === A player can choose to Pass and take no action during his turn, even if he still has available Crewmen. In this case, the player cannot take any more actions during this round. If a player has no available Crewmen, he automatically passes. == END OF THE GAME == The game ends either: at the end of the Action Phase of the tenth round or if all of the Expeditions have returned to Greenland. In addition to the point earned during the game, players receive more Prestige points based on the tokens they picked up along the way. ==== Exploration Points: ==== Point are awarded to the player(s) with the most, second most, and third most Shipwrecks, Straits, and Cartography tokens (see tooltips for scoring). ==== Set Points: ==== Each set is composed of one token of each Discovery type – one Inuit, one Cairn, one Shipwreck, one Strait, and one Cartography token. Each set gives a bonus of 6 prestige points. ==== Abandonment Penalty: ==== Each Sled and/or Ship which did not return to Greenland before the end of the tenth Exploration season is lost. When a Ship or a Sled is lost, all of the crewmen in the corresponding column are lost with it. Every lost Ship and/or Sled gives a penalty of -2 Prestige points for each corresponding lost Crewman. An abandoned ship gives a penalty of -2 Prestige points. b1ae8800a8556d2ec55b52cf4109d6778adb2b76 Gamehelpquoridor 0 102 874 695 2013-06-02T21:22:24Z Januszk0 3262 wikitext text/x-wiki W grze może brać udział dwóch lub czterech graczy. Plansza złożona jest z 81 kwadratów, po których poruszają się pionki reprezentujące graczy. Na początku rozgrywki pionki należy ustawić po środku przeciwległych krawędzi planszy. Celem gry jest zostaniem pierwszym graczem, który dotrze do startowego rzędu przeciwnika. Można to uczynić poruszając się o jedno pole w 4 kierunkach lub stawiając ogrodzenia uniemożliwiające przejście. W rozgrywce dwuosobowej każdy posiada 10 drewnianych płytek, a w rozgrywce czteroosobowej po 5. Gracze wykonują ruch jeden po drugim w kolejności według ruchów zegara (pierwszy gracz jest losowany). Nie wolno całkowicie zablokować przeciwnika - zawsze musi istnieć przynajmniej jedno przejście. Istnieje także zasada mówiąca o możliwości przeskoczenia pionka przeciwnika w sytuacji, gdy stoją na przeciwko siebie w sąsiadujących polach. b2d49d0979a63aeb3a50507fff8df67ace68c411 889 874 2013-06-16T13:41:18Z Issity 2112 Undo revision 874 by [[Special:Contributions/Januszk0|Januszk0]] ([[User talk:Januszk0|talk]]) - Polish text was placed on English page wikitext text/x-wiki [http://heyjude0929.pixnet.net/blog/post/26923277-%5B%E9%81%8A%E6%88%B2%E4%BB%8B%E7%B4%B9%5D%E6%AD%A5%E6%AD%A5%E7%82%BA%E7%87%9Fquoridor]步步為營(zh) Corridor is a 2-player game, the object of which is to move your pawn to the other side of a 9x9 board before your opponent. You can either move your man up,down or sideways or put a fence down. Players can jump over an opponent that he is next to or jump diagonally if he is next to a fence. Players cannot jump over fences. Players can put a fence down to block an opponent, but must allow at least one path to get to his winning side. You have only 10 fences. There is a fence counter by your name. Set traps to delay the opponent and create corridors to a winning path. You can concede the game by clicking on the square by your name and clicking "Concede...". You must have completed 50% or more of your game. There is an optional starting "Off the Center Aisle" Variation. Players agree to play this variation. The first player moves sideways and the second player move sideways in the opposite direction. The players keep on moving sideways until they agree to stop. 81f6185c44e295d3d224159201b0b4d3a368fc64 Gamehelpdragonheart 0 31 875 811 2013-06-07T07:45:17Z Guszty 3271 wikitext text/x-wiki ==Summary== Dragonheart is a two player card game where the goal is to collect as many points as possible by placing your cards on the board and collecting the cards already placed there. There are 9 different cards, each with their own rules as to which cards can be collected when they are placed down. ==Goal== To have the most points by the end of the game. ==Game Start== At the start of the game, each player takes five cards from their deck and the Great Dragon piece is put on the board. ==End== The game ends when all nine ship cards have been used, or one players deck is exhausted. At that point, each player adds the value of all the cards in their respective piles, plus 3 extra points to whomever has the Great Dragon piece. Whoever has the most points, wins the game. In case of a tie, the player with the Great Dragon wins. ==Turns== Players alternate turns, putting down and picking up cards. A player may choose to put down as many cards of the same type as he or she wants to. The cards get placed on their corresponding picture on the game board. Depending on what was placed, a player may take cards from the board to put in their pile, or move cards to the bottom of the board, below the ship. ==Cards== There are nine types of cards in the game. Cards have a point value to them listed as a number in the corner, that is unrelated to what type of card it is. Arrows on the board indicate what cards can take what. * ''Dwarf'': The player who places the fourth dwarf on the board, takes all four of the dwarf cards. No other card allows a player to take a dwarf. * ''Huntress'': The player who places the third huntress card, takes all fire dragon cards from the board. At that point, the three huntress cards are moved to the bottom of the board. * ''Fire Dragon'': Each time a fire dragon is placed, the player takes all of the treasure chest cards. * ''Treasure Chest'': Placing this card has no action, but it can be taken by a fire dragon or sorceress. * ''Troll'': Placing this card allows the player to take the sorceress pile. * ''Knight'': The player who places the second knight card, may choose to take either the sorceress or troll pile. Afterwards, the two knight cards are placed at the bottom of the board. * ''Petrified Dragon'': Placing this card has no effect, but when it is taken by a sorceress, the Great Dragon piece is taken too, either from the board or from the opponent. * ''Sorceress'': Placing a sorceress allows the player to take either the petrified dragon pile, or the treasure pile. Taking the petrified dragon cards (if there are any on the board) gives the player possession of the Great Dragon. * ''Ship'': The third ship card placed, allows the player to take all the knight and huntress cards placed at the bottom of the table. The ship cards are then discarded. The third time that three ship cards are played ends the game and the opponent takes one final turn. * ''Great Dragon'': The great dragon piece goes to the last player to take a petrified dragon stack. Possession of the Great Dragon gives the player an extra card, plus three extra points at the end of the game. If the dragon is taken from an opponent, then the player gets a random card from the opponents hand. e483577692cda37d861be7c5ae5fb36d9746d21a Club Board Game Arena 0 8 876 20 2013-06-10T20:09:36Z Sourisdudesert 1 wikitext text/x-wiki ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classical approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena chooses to set 3 fixed amounts for donations in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, your get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allow you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! ff7997026610fbb6d05400848cd31c5206035eea Gamehelppylos 0 125 877 2013-06-11T07:00:32Z Stst 2180 Created page with "'''PLAYING A GAME''' Start of the game Each player alternately, puts a ball from his reserve into any hollow which he has chosen. '''Stacking on a square''' When one or more ..." wikitext text/x-wiki '''PLAYING A GAME''' Start of the game Each player alternately, puts a ball from his reserve into any hollow which he has chosen. '''Stacking on a square''' When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: - taking a ball from his reserve and placing it on the board, - placing a ball from his reserve on one of the squares of balls, - moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. '''Square in the player's own color''' A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). '''END OF THE GAME''' The winner is the one who places his last ball at the top of the pyramid '''CHILDREN’S VERSION''' To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. '''VERSION FOR MATURE PLAYERS: EXTENDED''' A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: - 4 balls of the same color in line on the first level, - 3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two spheres can be taken back per shot. ee24a02786352eff33532674147b377d20f947bd 878 877 2013-06-11T07:06:04Z Stst 2180 wikitext text/x-wiki '''PLAYING A GAME''' Start of the game Each player alternately, puts a ball from his reserve into any hollow which he has chosen. '''Stacking on a square''' When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. '''Square in the player's own color''' A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). '''END OF THE GAME''' The winner is the one who places his last ball at the top of the pyramid '''CHILDREN’S VERSION''' To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. '''VERSION FOR MATURE PLAYERS: EXTENDED''' A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two spheres can be taken back per shot. 299c9719ba294c06ae48d56d5b896b41cd64edee 879 878 2013-06-11T07:07:35Z Stst 2180 wikitext text/x-wiki ===PLAYING A GAME=== Start of the game Each player alternately, puts a ball from his reserve into any hollow which he has chosen. ===Stacking on a square=== When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. ===Square in the player's own color=== A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). ===END OF THE GAME=== The winner is the one who places his last ball at the top of the pyramid ===CHILDREN’S VERSION=== To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. ===VERSION FOR MATURE PLAYERS: EXTENDED=== A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two spheres can be taken back per shot. d0e1ce0f661f3b20511133289fa4c7a15d576504 880 879 2013-06-11T07:08:18Z Stst 2180 /* PLAYING A GAME */ wikitext text/x-wiki ===PLAYING A GAME=== Start of the game. Each player alternately, puts a ball from his reserve into any hollow which he has chosen. ===Stacking on a square=== When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. ===Square in the player's own color=== A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). ===END OF THE GAME=== The winner is the one who places his last ball at the top of the pyramid ===CHILDREN’S VERSION=== To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. ===VERSION FOR MATURE PLAYERS: EXTENDED=== A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two spheres can be taken back per shot. b47a23568de7da199140660a582cabae45f7c3b2 881 880 2013-06-11T07:09:18Z Stst 2180 /* END OF THE GAME */ wikitext text/x-wiki ===PLAYING A GAME=== Start of the game. Each player alternately, puts a ball from his reserve into any hollow which he has chosen. ===Stacking on a square=== When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. ===Square in the player's own color=== A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). ===END OF THE GAME=== The winner is the one who places his last ball at the top of the pyramid. ===CHILDREN’S VERSION=== To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. ===VERSION FOR MATURE PLAYERS: EXTENDED=== A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two spheres can be taken back per shot. 0873de88f4bad1c0ad90275ddb11bb3c4fc3bf33 882 881 2013-06-11T11:31:49Z Stst 2180 /* VERSION FOR MATURE PLAYERS: EXTENDED */ wikitext text/x-wiki ===PLAYING A GAME=== Start of the game. Each player alternately, puts a ball from his reserve into any hollow which he has chosen. ===Stacking on a square=== When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. ===Square in the player's own color=== A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). ===END OF THE GAME=== The winner is the one who places his last ball at the top of the pyramid. ===CHILDREN’S VERSION=== To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. ===VERSION FOR MATURE PLAYERS: EXTENDED=== A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two balls can be taken back per shot. cb99b4160b207e2265cc6194e3dd4073a76c87f3 883 882 2013-06-11T20:44:47Z Stst 2180 /* VERSION FOR MATURE PLAYERS: EXTENDED */ wikitext text/x-wiki ===PLAYING A GAME=== Start of the game. Each player alternately, puts a ball from his reserve into any hollow which he has chosen. ===Stacking on a square=== When one or more squares of balls are formed on the board or at higher levels, a player can choose to stack one of his balls on it; when it is his turn to play, he then has a choice between: *taking a ball from his reserve and placing it on the board, *placing a ball from his reserve on one of the squares of balls, *moving one of his balls already on the board and putting it on a square of balls, but only if this move raises his ball by one or more levels. This move enables him to save a ball in his reserve. A ball on the board cannot be moved if it is already supporting another balls. ===Square in the player's own color=== A player who makes a square of balls in his own color immediately takes back either one or two of his balls from the board and puts them back in his reserve; He may recover any ball belonging to him by picking them up from any level of the pyramid - including the one which he has just placed - except for those balls that support other balls. (Making several squares of balls in his own color by putting on one ball only allows the player to withdraw one or two of his balls). ===END OF THE GAME=== The winner is the one who places his last ball at the top of the pyramid. ===CHILDREN’S VERSION=== To start gradually, it is possible to play without using the rules for the "square in the player's own color " - only the rules for stacking allow the player to save balls. ===VERSION FOR MATURE PLAYERS: ADVANCED=== A player takes back one or two of his balls which are on the board when he makes a square or a line in his color: To be valid, alignments must be on either the first or the second level. An alignment consists of: *4 balls of the same color in line on the first level, *3 balls of the same color in line on the second level. A diagonal line is not an alignment. No more than two balls can be taken back per shot. e726a8d5e6806f118954889b3f41813310ed8709 Gamehelpunitedsquare 0 42 884 707 2013-06-12T05:36:09Z Chaotic iak 3338 wikitext text/x-wiki ==Goal== The goal is to be the player who has the most squares of his color. ==Rules== The game uses pieces of six types, where each piece is in shape of a square. The sides of each piece are colored with one of red, yellow, green, blue, so that a piece has one side for each color. Each "side" appears as a triangle. Each player on his turn puts a square next to a square already on the board. The piece may be rotated, but not reflected. The sides coinciding with previous pieces on the board must match in color. It's possible for a blank square to be adjacent to two sides of the same color, blocking said square from being occupied (because there is no piece that has two sides of the same color). When a player cannot put any piece, the turn is passed. In all other cases, the player must put some piece. Due to the appearance of a side as a triangle, matched sides form a tilted square. Each of these squares scores a point for the person holding this color. ==Ending the game== When no more piece can be put by any player, the player with the most squares of his color wins. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''HAVE A GOOD GAME!''' 941de17dad67e96ffda31089a82f4ccbdcdacc23 Gamehelpelfenland 0 126 885 2013-06-16T12:50:55Z Een 3 Created page with "== Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. == R..." wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 368bc2b9b805fc1e45ba2d5b8717bf20d08e1285 886 885 2013-06-16T12:52:46Z Een 3 /* Round overview */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 5e2dabcee0f80719357b72ee48d9f8c5067a45fb 887 886 2013-06-16T12:57:28Z Een 3 /* Goal of the game */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the counters from other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 3c9887d2684d2aa2d6260a89e25c0ee3d74738fd 888 887 2013-06-16T13:00:56Z Een 3 /* Goal of the game */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 5109a7a3a58a66f70c401dfa41eab700fceabcdf Gamehelpyatzy 0 122 890 842 2013-06-19T05:07:45Z Wonderer 3397 wikitext text/x-wiki Be aware the rules linked from Wikipedia are NOT the rules implemented. The rules implemented vary a great deal from the real rules, and make the game much more random and luck-based. The rules for 3 of a kind, 4 of a kind, small straight, and large straight are all different. In this implementation 3 and 4 of a kind only counts the dice used in the pairing, not the other dice. This makes the odds identical for both straights, but has the large still worth more points. 08961c163bea1dc1e7722529d8499e07e5d63525 891 890 2013-06-19T05:11:03Z Wonderer 3397 Blanked the page wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Studio 0 49 892 793 2013-06-20T19:19:17Z Sourisdudesert 1 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) See announcement here: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973 == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio logs]] * [[Studio back-office]] * [[Studio FAQ]] == BGA Developer team organization == * [[Steps to create a BGA game]] * [[Post-release phase]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] b5935f96db32b7aa0f0a029e5a5011b29c677061 Studio logs 0 127 893 2013-06-20T19:19:29Z Sourisdudesert 1 Created page with "(to be written)" wikitext text/x-wiki (to be written) a5a10fffe3d6647651bdea1a52be1345e04bd462 894 893 2013-06-20T19:58:36Z Sourisdudesert 1 wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). 4123c3426fa68031dd4dd97a71e1dccfe2d70d90 895 894 2013-06-20T19:58:49Z Sourisdudesert 1 /* Request responses */ wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). 7857032caa340af1ad58d8a4f03e59f814f40b19 896 895 2013-06-20T20:02:52Z Sourisdudesert 1 /* BGA request&SQL logs */ wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == This log is useful: * When you want to check what SQL requests has been built during a request. * When you want to debug your PHP code using "self::trace" * When you want to know why a request takes too many time. On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). === SQL requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] 0.26 SELECT player_tokenColor FROM player WHERE player_id ='4' All requests to Database are traced in this log. You can see here the time take by the request (0,26ms). 5fb44e06a4378b1bc27d1ef9de818ff079a6161f 897 896 2013-06-20T20:06:31Z Sourisdudesert 1 /* SQL requests */ wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == This log is useful: * When you want to check what SQL requests has been built during a request. * When you want to debug your PHP code using "self::trace" * When you want to know why a request takes too many time. On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). === SQL requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] 0.26 SELECT player_tokenColor FROM player WHERE player_id ='4' All requests to Database are traced in this log. You can see here the time take by the request (0,26ms). === Custom trace === You can use special PHP methods in your PHP code to left some trace in this log: * self::trace( "your message here" ); // Display "your message here" in the log * self::dump( "My variable", $variable_to_dump ); // Display the content of $variable_to_dump in the log 9e994b9737a804e7ab1d1bfdb69c6c00e48a06df 898 897 2013-06-20T20:07:24Z Sourisdudesert 1 wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == This log is useful: * When you want to check what SQL requests has been built during a request. * When you want to debug your PHP code using "self::trace" * When you want to know why a request takes too many time. On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). === SQL requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] 0.26 SELECT player_tokenColor FROM player WHERE player_id ='4' All requests to Database are traced in this log. You can see here the time take by the request (0,26ms). === Custom trace === You can use special PHP methods in your PHP code to left some trace in this log: * self::trace( "your message here" ); // Display "your message here" in the log * self::dump( "My variable", $variable_to_dump ); // Display the content of $variable_to_dump in the log == BGA unexpected exceptions logs == 37bc965f13f59e5a37e9b60206ed34bc5ab9d53b 899 898 2013-06-20T20:10:01Z Sourisdudesert 1 /* BGA unexpected exceptions logs */ wikitext text/x-wiki BGA Studio logs are available directly from your game development interface. Logs allows you to check out what happened recently on server and to debug your game. == BGA request&SQL logs == This log is useful: * When you want to check what SQL requests has been built during a request. * When you want to debug your PHP code using "self::trace" * When you want to know why a request takes too many time. On this log, you can see: === Your requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 Note that the best way to check your Ajax request is to read the [[http://en.doc.boardgamearena.com/Tools_and_tips_of_BGA_Studio#Input.2FOutput_debugging_section Input/Output section]]. === Request responses === Example: 20/06 21:50:56 [notice] [T403] [4/mytest0] OK-0 169 d141 c8 e0 I9 A158 V0 T0 /cinco/cinco/exchange4Cards.html?id=4&lock=97d1c7a1-903a-4d1f-8206-de39ce8204fc&table=403&testuser=4&dojo.preventCache=1371757856044 You can recognize a response because it contains [notice]. Usually, there is one response for each request. Let's details the beginning of the log: * 20/06 21:50:56: the date * [notice] * [T403]: this is a log from table 403 * [4/mytest0]: this is use "mytest0", with id 4 * OK-0: it means that the request ended up successfully, with no exception (expected or unexpected). * 169: this is the time taken to process the request (169ms). * d141: this is the total Database time used to process the request (141ms). === SQL requests === Example: 20/06 21:50:56 [info] [T403] [4/mytest0] 0.26 SELECT player_tokenColor FROM player WHERE player_id ='4' All requests to Database are traced in this log. You can see here the time take by the request (0,26ms). === Custom trace === You can use special PHP methods in your PHP code to left some trace in this log: * self::trace( "your message here" ); // Display "your message here" in the log * self::dump( "My variable", $variable_to_dump ); // Display the content of $variable_to_dump in the log == BGA unexpected exceptions logs == In this log you can check the last Unexpected exceptions from your game. Exceptions management on PHP side [[http://en.doc.boardgamearena.com/Main_game_logic:_yourgamename.game.php#Managing_errors_and_exceptions is described here]]. The log displayed the complete stacktrace of the exception, so you can debug it. b302ba731048fb2cebae26604f24b8d7f1941057 Gamehelpseasons 0 43 900 844 2013-06-22T20:25:45Z Charlow 3423 wikitext text/x-wiki Seasons is a game of generating points (crystals) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: ''Star'' - increase the maximum card player can summon (max 15) ''Elements'' - Gain an energy of the element shown (water, earth, air, fire) ''Numbers'' - Gain the number of Crystals as indicated by the number ''Square card'' - Draw a card ''Dice with frames surrounding'' - Allow user to transmute energy into crystal (depend on which part of the game is at) ''Dots'' - Indicate how fast the marker progresses through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There are 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each played card and the crystal owned and substracting 5 points for each power card remaining in hand. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. == Difficulty levels: == '''Apprentice wizard level''' In order to make the game easier to learn, there are “pre-constructed” sets of nine power cards. Instead of taking step 1 of the first game phase, each player gets one of these sets. '''Magician level''' Only the power cards numbered 1 to 30 are in play. These cards have easy to grasp effects and will allow you to slowly discover the world of Seasons. '''Archmage level''' All 50 kinds of power cards are in play. The cards numbered 31 through 50 have more complex effects than the basic cards, but will allow you to extend the fun of playing by discovering new effects and combinations. 5a76f838e4388603a11a1166cb6bacfaecc16b97 Game interface logic: yourgamename.js 0 88 901 834 2013-06-30T13:34:16Z Een 3 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board" ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> f6a8e63abe8a7466d9a3479fed629a109b8456ee Game interface logic: yourgamename.js 0 88 902 901 2013-06-30T13:35:10Z Een 3 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onClick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 33ef921054092140ab755e1a5f8911af4ec21d61 Gamehelpelfenland 0 126 903 888 2013-07-06T22:29:06Z Spacediver 718 /* Goal of the game */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. Any players having equal number of Town Pieces are ranked by remaining cards (the more the better). We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' a06379653e76e9da044516d6027676e8ace27ccb 924 903 2013-08-18T05:23:21Z Chazm 3718 /* Goal of the game */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. Any players having an equal number of Town Pieces are ranked by remaining cards (the more the better). We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or drawn from the facedown stack, until each player took 3. # One after another, each player chooses must plan a travel road by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to up the cost of traveling on this road from 1 card. # One after another, each player is going to travel from town to town. The current position of a player is shown by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 52928b7fae91e13764e651d3e3c77b390c33e891 925 924 2013-08-18T05:33:30Z Chazm 3718 /* Round overview */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. Any players having an equal number of Town Pieces are ranked by remaining cards (the more the better). We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or draws from the facedown stack, until each player has taken 3. # One after another, each player must plan a travel route by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to increase the cost of travel by an addtional card. # One after another, each player travels from town to town. The current position of a player is indicated by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes. To travel on a road, a river or a lake, the player needs to pay with the cards in hands the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (NB: the 'Magic Cloud' Transportation Counter cannot be used to travel in the desert, as it's too hot for a Cloud over there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 604d9ffb19ecc5618c7a2ad55816fe5a2cc215f1 926 925 2013-08-18T05:37:14Z Chazm 3718 /* Travel rules */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. Any players having an equal number of Town Pieces are ranked by remaining cards (the more the better). We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or draws from the facedown stack, until each player has taken 3. # One after another, each player must plan a travel route by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to increase the cost of travel by an addtional card. # One after another, each player travels from town to town. The current position of a player is indicated by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes To travel on a road, a river or a lake, the player needs to pay with the cards in hand the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (Note: the 'Magic Cloud' Transportation Counter cannot be placed in the desert, as it's too hot for a Cloud there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactic depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' c57cd8daa62a4072f5c756b58bbe2784d44635f7 927 926 2013-08-18T05:38:33Z Chazm 3718 /* Variant */ wikitext text/x-wiki == Goal of the game == Visiting as many cities as possible in Elfenland over 4 rounds. At the end of the 4th round, the player who collected the most Town Pieces wins. Any players having an equal number of Town Pieces are ranked by remaining cards (the more the better). We advise you to play with at least 3 players to best enjoy the game (4 or 5 is even better). A lot of the fun comes from using the Transportation Counters set by other players to optimise one's travels, so the more the merrier! == Round overview == A round is split into phases: # Each player fills his hand up to 8 cards and gets a secret Transportation Counter. # One after another, each player chooses a Transportation Counter from the 5 visible or draws from the facedown stack, until each player has taken 3. # One after another, each player must plan a travel route by laying a Transportation Counter matching the terrain of the chosen road. This phase goes on until each player has passed consecutively. Each player also owns an Obstacle that he can lay down on a Transportation Counter to increase the cost of travel by an addtional card. # One after another, each player travels from town to town. The current position of a player is indicated by the Elf Boot of his color, initially set on the biggest town, Elvenhold. At the end of the round, a player can keep a maximum of 4 cards in hand, and keeps only 1 Transportation counter. == Travel rules == Players can travel : * on the roads on which a Transportation Counter has been set * on the rivers * on the lakes To travel on a road, a river or a lake, the player needs to pay with the cards in hand the cost matching the terrain of the road and the means of transport used. For example, if the Transportation Counter 'Magic Cloud' has been set on a mountain road, one can travel with just one 'Magic Cloud' card. But if it has been set on a forest or plain road, one needs to pay 2 'Magic Cloud' cards to pass (Note: the 'Magic Cloud' Transportation Counter cannot be placed in the desert, as it's too hot for a Cloud there). Also : * To travel a river, you need only 1 'Raft' card to go downriver, but you need 2 to go upriver (look for the small arrows). * To cross a lake, you need to pay two 'Raft' cards. * If an Obstacle has been set on a road, you need to pay 1 more card of the appropriate type. * If you don't have the appropriate cards to travel on a road, you can still pass using a 'Caravan', by paying 3 cards of any type (4 cards if there is an Obstacle on the road). == Variant == In the 'Hometown' variant, each player gets a secret town card at the beginning of the game, telling him which town he has to reach at the end of the 4th round. If he doesn't manage to get there, he'll lose as many points as the remaining distance he has to travel to get there. This variant gives more tactical depth to the game, as you have to plan each round to the best to get the most Town Pieces while making sure to be able to get to your hometown at the end of the game. '''Have a good game!''' 908794728060afee29a7eab7dd4e91cc500b128b Reputation 0 17 904 165 2013-07-09T05:27:59Z Jest phulin 3510 /* Advice: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png */ wikitext text/x-wiki [[Category:Help]] == What is reputation ? == On '''Board Game Arena''', we would like to have a strong competitive atmosphere with a respectful and fair play ambiance. To achieve this goal, each player has a '''reputation profile''' which is the representation of his general attitude with others players. This profile is composed of 3 items: * Opinions from other players (http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png) * % of games you finished * % of games you finished with no clock penalties Seeing the reputation profile of a player, you are able to check if his behavior is good, if he won't quit the game before the end, and if he respect time limits. == How to increase my reputation / what makes my reputation decrease ? == At any time, you can give others players http://fr.boardgamearena.com/theme/img/common/reputation_up.png and http://fr.boardgamearena.com/theme/img/common/reputation_down.png marks. * a http://fr.boardgamearena.com/theme/img/common/reputation_up.png if you liked to play with him/her, and recommend to play with this player. * a http://fr.boardgamearena.com/theme/img/common/reputation_down.png if you disliked to play with him/her, and discourage others to play with this player. You can only give a single thumb (green or red) to a single player. If your opinion evolved you can click again on a thumb to reflect this change. As soon as you give a "red thumb" to a player, a warning message is displayed when you try to join a table where this player is. In this situation it is recommended to leave the game or to expel this player. By nature, opinions are subjectives. There is nothing we can do for you if you receive a for an unfair reason. However, we know by experience that this system has a lot of advantages and give a relevant information. Qn: its unclear if the "> 10% unfavorable" warning is calculated against the number of "thumbs up / thumbs down".. or "total number of unique players played with / thumbs down". Hope the formula is the latter.. as the first formula will raise many more warnings.. more idiots give thumbs down, and are lazier to give thumb up. == Advice: how to get plenty of http://fr.boardgamearena.com/theme/img/common/reputation_up.png == * Be polite! Say at least "hello" and "good luck" at the beginning of the game, and maybe "good game" ("gg") at the end of the game. * When you need some time to think, click on "I would like to think a little" link. * Always stay calm: it's a game. * Don't press your opponent to play if he has some time left. * If you really can't finish the game (this is not supposed to happen...), then say you are sorry and leave the game (don't wait to get expelled) to save your opponents time. Voluntarily disconnecting and conceding victory to the opponent does not cost you reputation like being expelled does. * Be a good loser: if your lose because of bad luck or a strategy you dislike, don't blame the luck or your opponent (and don't give him a http://fr.boardgamearena.com/theme/img/common/reputation_down.png for this reason!). * Be a good winner: if your opponent makes a mistake you can signal it to him, but avoid triumphalism and provocation. == What are the consequences of a bad reputation ? == A bad reputation makes you suspect to your potential opponents. You will have to explain your situation, and maybe some players won't take the risk to play with you. It is also possible to filter players by reputation on a table. The worse your reputation is, the bigger difficulties you will have to find opponents. 67cf8fc8799861af33ddd3c786489e404d2ed3fb Privacy 0 128 905 2013-07-09T06:22:06Z Een 3 Created page with "To create an account, you need to provide an email, user name and password. You can also complete your player profile with some other data. We store this data. Yep. It would ..." wikitext text/x-wiki To create an account, you need to provide an email, user name and password. You can also complete your player profile with some other data. We store this data. Yep. It would be pretty annoying for you to enter it each time you visit ;) If you are 'internet paranoid' and you never provide any website with accurate personal information, congratulations! You can skip this section. If you are not, congratulations! You can also skip this section which is intended only for 'internet paranoid' people anyway... It has been written for compliance with some social network authentication policy (take your bet, there is only two - oh, wait, if you are reading this you probably know). Well, to be fair, I could have left it empty too, the only check made by said social network being that the page exists. But, what would have been the fun in that? If you are still interested to read on some stuff instead of going straight to playing: * Your password is stored encrypted, but the usual precautionary measures apply. You should use a strong password and not use the same one that for sensitive websites. Some people would say never to use the same password, but this looks like excessive blunt force to the head, except for websites with very sensitive information. Shouldn't be the case on a gaming website, but hey, do as you please. * We don't share your email with third parties without your consent. Your consent may be implicit in some obvious cases (such as if you take part in a contest with prizes from a publisher, as it will be needed for the publisher to contact you to redeem the prize). * Your profile information is visible to other players, as well as your gaming history on the website. So, since anyone can create an account on the site, it should be considered public information. But if you use a pseudonym for your username, it shouldn't be easy to link it to your real identity (in case you would be worried about that). Hope this all sounds reasonable to you, and that you can now enjoy Board Game Arena without second thoughts! Cheers and doughnuts, The admins. 936ac1f56e7544f713799cdf86cc4e3b858babbf The Boss 0 46 906 423 2013-07-10T03:32:08Z Stationary 3106 wikitext text/x-wiki == Goal == At the end of the hand, the player with the most gangsters in the city wins the "city card" that was placed face down at the beginning of the hand. Be the player with the most money at the end of the game! == Description == Each city, except '''Chicago''', has one hidden card underneath it. The rest of the cards are distributed among the players, giving each player information on which card is hidden under each city. Each round, each player: 1) May place gangsters on a city. The large gangsters (Experts) are returned at the end of each round, while the small (Occasionalists) ones are not. After placing the gangsters, you must have more of them than any opponent on that city. 2) Must play a card from your hand, revealing that information about the city. When everybody has 2 cards in their hand, the police card is turned over. If there are 3 police symbols with the same color turned over, the game ends after the hand in progress is finished. When everybody is out of cards, reveal the face down cards. Whoever has the most gangsters on the city 'wins' that face down card. If it is money, it is worth that many points. If it is a gun one of your big gangsters is killed, the bars mean they go to jail for 2 rounds and the red cross sign is hospital for 1 turn. If you get the crossed out man card in Cincinnati you are banned from playing gangsters there the rest of the game. '''Chicago''' Chicago doesn't have a hidden card, instead it is worth one half (rounded down) of the sum of the last cards played from a player's hand on each of the cities to its left, it moves one space right each round. == Beginner Tips == *Give out useless information early and hold on to your gangsters until seeing what your opponents are planning. *If you have the gun for Detroit or Kansas City you KNOW that city will be worth some points. This isn't true for Philadelphia which could still send you to jail. 31c2382f9f02f6aec6f8b1aa93cb206bb8f445ff Gamehelpreversi 0 124 907 868 2013-07-15T21:15:37Z Januszk0 3262 Opis wikitext text/x-wiki Othello znane również pod nazwą Reversi to wciągająca gra logiczna, której korzenie sięgają XVIII wieku. W grę od XIX wieku gra niemal cała Europa. O jej wielkiej popularności zadecydowały proste reguły (prostsze niż warcabów) i bogactwo kombinacyjne dorównujące szachom. Od tych ostatnich gra jest jednak zdecydowanie bardziej dynamiczna. == Zasady gry: == Rozgrywka toczy się na kwadratowej planszy podzielonej na 64 pola (8x8). Każdy z graczy ma dostęp do wspólnego zestawu dwustronnych pionków (zwyczajowo, z jedną stroną białą, a drugą czarną). Gracze na zmianę wykonują ruchy dokładając na planszy pionki w swoim kolorze. Pionki należy układać w taki sposób, aby pomiędzy pionkami gracza (poziomo, pionowo lub na skos) znalazła się jak największa ilość pionków przeciwnika. Po wykonaniu ruchu, wszystkie te pionki zostają przejęte i zmieniają kolor. Jeżeli gracz nie może wykonać dozwolonego przepisami posunięcia, musi pauzować (traci kolejkę). Celem gry jest ułożenie na planszy jak największej ilości pionków, tak aby zamienić pionki przeciwnika na własne. Wygrywa ten z graczy, którego większa liczba pionków znajduje się na planszy po zakończeniu gry. W przypadku równej ilości pionków, gra kończy się remisem. 27f5626440fd71b896475a030a6b7ec0076ee359 908 907 2013-07-15T21:19:16Z Januszk0 3262 Blanked the page wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 909 908 2013-07-15T21:20:35Z Januszk0 3262 wikitext text/x-wiki [http://en.wikipedia.org/wiki/Reversi] fe37062068d582dcb198fb6c0e2dff2371bc9a86 910 909 2013-07-15T21:25:51Z Januszk0 3262 wikitext text/x-wiki [English Rules: Link][http://en.wikipedia.org/wiki/Reversi] 245acab3bf59561ee3167722bfe3b03c5fb3d769 Faq 0 3 911 659 2013-07-18T18:27:30Z Guszty 3271 wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 94dea1133bbc8858fde6d481215ca43bf31342b8 Gamehelpcantstop 0 35 912 757 2013-07-25T17:01:39Z Chaotic iak 3338 wikitext text/x-wiki Can't Stop is a game involving rolling dice and knowing when to stop pushing one's luck. ==Rules== Can't Stop is played with a board consisting of 11 columns, numbered from 2 to 12, four dice, and three temporary pieces. ===Splitting dice=== On a player's turn, they roll the four dice, split it to two groups however they like, then advance a step on each column whose sum is equal to some group of the dice. Examples: * Roll: 3,3,4,4. One can split it to 3,3 and 4,4, advancing a step on each of columns 6 and 8, or 3,4 and 3,4, advancing two steps on column 7. * Roll: 2,3,4,5. One can split it in three ways: 2,3 and 4,5 (columns 5 and 9), 2,4 and 3,5 (columns 6 and 8), or 2,5 and 3,4 (columns 7 and 7). * Roll: 4,4,4,6. There is only one way to split it, 4,4 and 4,6, which means columns 8 and 10. * Roll: 1,1,1,1. There is only one way to split it, 1,1 and 1,1, which means columns 2 and 2. ===Temporary pieces=== In a turn, a player may only advance in three columns. The temporary pieces are to help the player in keeping track their progresses. However, one must make as many moves as possible. Examples: * One hasn't moved at all and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 5,9. They may not choose to advance only on column 5 or only on column 9 because they must perform all moves possible. * One has moved on columns 4 and 5 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 4,5,9. * One has moved on columns 2 and 3 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, and so should have advanced on columns 5 and 9. But this is not allowed; it makes them advance on four columns. They must pick either 5 or 9 to advance from. * One has moved on columns 2, 3, and 12 and rolled 2,3,4,5. No matter how they split the dice, they cannot advance. ===Stopping one's turn=== A player keeps their turn until they state to stop or they cannot advance (like the last of the examples above). In the former case, the temporary pieces are made permanent; they will continue from the positions they have now. In the latter case, the temporary pieces are removed; their position is rolled back to before they have started their turn. Examples: * One decides to stop after advancing 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9. Now their position is 4 steps on column 6, 3 steps on column 7, 2 steps on column 8, and 4 steps on column 9. * One advanced 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9, but then cannot advance. Their position is rolled back to only 4 steps on column 6 and 3 steps on column 9. ===Completing a column=== When one decides to stop and they have reached the end of some column, that column is scored for that player and no other player may advance in that column. For example, if column 5 has been scored and one splits a 2,3,4,5 to 2,3 and 4,5, they cannot advance on column 5 because it has been scored. ===Winning condition=== When one scores three columns, they win. ==Strategy== The strategy of the game revolves on knowing when to stop. As one keeps playing, they still have the risk of losing all progress on the turn; stopping will save the progress but gives the other players a turn each. Knowing which is more beneficial helps. Columns with more extreme numbers are less probable (a 2 is only scored by a 1,1 and a 12 is only scored by a 6,6, while a 7 is scored by six pairs of dice), so they have less steps to work on. One's strategy can also revolve on choosing whether to aim for middle columns or extreme columns. b66171fad98a092b617274bc96550af3a344bd51 913 912 2013-08-03T21:30:44Z Theotherk 3645 /* Splitting dice */ wikitext text/x-wiki Can't Stop is a game involving rolling dice and knowing when to stop pushing one's luck. ==Rules== Can't Stop is played with a board consisting of 11 columns, numbered from 2 to 12, four dice, and three temporary pieces. ===Splitting dice=== On a player's turn, they roll the four dice, split the four dice to two groups of two dice however they like, then advance a step on each column whose sum is equal to some group of the dice. Examples: * Roll: 3,3,4,4. One can split it to 3,3 and 4,4, advancing a step on each of columns 6 and 8, or 3,4 and 3,4, advancing two steps on column 7. * Roll: 2,3,4,5. One can split it in three ways: 2,3 and 4,5 (columns 5 and 9), 2,4 and 3,5 (columns 6 and 8), or 2,5 and 3,4 (columns 7 and 7). * Roll: 4,4,4,6. There is only one way to split it, 4,4 and 4,6, which means columns 8 and 10. * Roll: 1,1,1,1. There is only one way to split it, 1,1 and 1,1, which means columns 2 and 2. ===Temporary pieces=== In a turn, a player may only advance in three columns. The temporary pieces are to help the player in keeping track their progresses. However, one must make as many moves as possible. Examples: * One hasn't moved at all and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 5,9. They may not choose to advance only on column 5 or only on column 9 because they must perform all moves possible. * One has moved on columns 4 and 5 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 4,5,9. * One has moved on columns 2 and 3 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, and so should have advanced on columns 5 and 9. But this is not allowed; it makes them advance on four columns. They must pick either 5 or 9 to advance from. * One has moved on columns 2, 3, and 12 and rolled 2,3,4,5. No matter how they split the dice, they cannot advance. ===Stopping one's turn=== A player keeps their turn until they state to stop or they cannot advance (like the last of the examples above). In the former case, the temporary pieces are made permanent; they will continue from the positions they have now. In the latter case, the temporary pieces are removed; their position is rolled back to before they have started their turn. Examples: * One decides to stop after advancing 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9. Now their position is 4 steps on column 6, 3 steps on column 7, 2 steps on column 8, and 4 steps on column 9. * One advanced 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9, but then cannot advance. Their position is rolled back to only 4 steps on column 6 and 3 steps on column 9. ===Completing a column=== When one decides to stop and they have reached the end of some column, that column is scored for that player and no other player may advance in that column. For example, if column 5 has been scored and one splits a 2,3,4,5 to 2,3 and 4,5, they cannot advance on column 5 because it has been scored. ===Winning condition=== When one scores three columns, they win. ==Strategy== The strategy of the game revolves on knowing when to stop. As one keeps playing, they still have the risk of losing all progress on the turn; stopping will save the progress but gives the other players a turn each. Knowing which is more beneficial helps. Columns with more extreme numbers are less probable (a 2 is only scored by a 1,1 and a 12 is only scored by a 6,6, while a 7 is scored by six pairs of dice), so they have less steps to work on. One's strategy can also revolve on choosing whether to aim for middle columns or extreme columns. 6de82fa759a9e714493e081febed1557c7456e7f Club Board Game Arena 0 8 914 876 2013-08-08T03:14:53Z Earl of essence 3660 /* Why can't you just have a standard donation system where I can choose any money amount ? */ wikitext text/x-wiki ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who have supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classic approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena has chosen to set 3 fixed amounts for donations, in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra : you don't need statistics to play and have fun, don't you ? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, your get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allow you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! 325eb81bb9bae3c84de8268c42ea6b12de222a88 915 914 2013-08-08T03:16:02Z Earl of essence 3660 /* Is it mandatory to join the club ? */ wikitext text/x-wiki ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who have supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classic approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena has chosen to set 3 fixed amounts for donations, in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra! You don't need statistics to play and have fun, do you? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, your get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allow you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! a9b1fab3effc1c29a439f7e5b1b53f26a00cad37 916 915 2013-08-08T03:17:13Z Earl of essence 3660 /* What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif */ wikitext text/x-wiki ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who have supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classic approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena has chosen to set 3 fixed amounts for donations, in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra! You don't need statistics to play and have fun, do you? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, you get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allows you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non-member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! b5ffe68707f95aedf17704f721af0a8070495584 917 916 2013-08-08T03:18:25Z Earl of essence 3660 /* What becomes of the money ? */ wikitext text/x-wiki ==Why can't you just have a standard donation system where I can choose any money amount ?== With this "club" system, we try to highlight players who have supported this website recently or do so on a regular basis. Depending on the amount of your donation, you are a member of the club for a given period of time. Many websites are using a more classic approach with a simple "donation box". By experience, we know that these websites rely on just a few generous users. Board Game Arena has chosen to set 3 fixed amounts for donations, in order to rely on a bigger number of small donors. ==Is it mandatory to join the club ?== Of course not. You can play for free without any limitation even if you are not a member of the club: Board Game Arena is a free service. Statistics are just an extra! You don't need statistics to play and have fun, do you? As a matter of fact, most players are not club members. ==What is a beginner account ? http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif == When you join Board Game Arena, you get a "beginner account" (http://en.boardgamearena.com/theme/img/accounttypes/beginner.gif) for 30 days. This beginner account allows you to view your own ELO ranking (http://en.boardgamearena.com/theme/img/common/rank.png) for each game. After 30 days, your account becomes a standard '''non-member''' account (http://en.boardgamearena.com/theme/img/accounttypes/free.gif). ==What becomes of the money ?== Board Game Arena's service is managed by a semi-professional team who needs money to make it run (in particular: hosting cost). Player donations are used to develop this website (new features, new games), to make it run (hosting, maintenance), to build the player community (events)... A big "thank you" to all members of the Board Game Arena Club whose contributions allow this website to exist for the enjoyment of everyone ! 57edae9e0c931f66c9fd4c5ab1c3ddd8b1f06828 Getting started 0 10 918 23 2013-08-08T03:23:22Z Earl of essence 3660 wikitext text/x-wiki [[Category:Help]] With '''Board Game Arena''' you can play within a few clicks. By choosing "[http://en.boardgamearena.com/#!lobby Play now]" on the left menu, you get a list of game tables waiting for players. You can join a game by clicking on "View table" then "Join game". If you prefer, you can create a new game table: click on the "Launch table" button that corresponds to the game you want to play, and wait for your opponents. Important to know before you start: * It's much better to join a table already created than to create a new one. * For most popular games you will find opponents at any time. For the others, try to connect around peak hour (around 22h CEST) to maximize your chances. * When you start a game you can't leave the table until it ends. If you leave you will receive a penalty which will create difficulties finding opponents later. a757320af56ced6014012dfea845fb0ec8c45592 Gamehelptzolkin 0 129 919 2013-08-09T14:34:28Z Edgar 3667 Created page with " == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''P..." wikitext text/x-wiki == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of themples from step you are on and all steps below -- ''End of Age(Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option. Flip your board to dark side up. -- This does not avoid food days. 4bfacd574a92eebe81d97b17b85b3702ebb652cc 920 919 2013-08-10T02:18:35Z Joseppr 1780 /* Advance the Calendar */ wikitext text/x-wiki == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of themples from step you are on and all steps below -- ''End of Age(Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option, flip your board to dark side up. -- This does not avoid food days. a93481001a7c82b23c85cda088a16424ec014722 921 920 2013-08-10T02:19:54Z Joseppr 1780 /* Food Day */ wikitext text/x-wiki == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of themples from step you are on and all steps below -- ''End of Age (Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option, flip your board to dark side up. -- This does not avoid food days. 492a51ebdd36dc9ecb3fa747a046c5a54b18ca80 922 921 2013-08-10T18:26:35Z Januszk0 3262 wikitext text/x-wiki == '''W każdej rundzie''' == - Gracze rozgrywają swoje tury zgodnie z ruchem wskazówek zegara - W Dniu Żywienia, gracze żywią robotników i otrzymują nagrody - Przestawiają kalendarz == '''Tury graczy''' == - '''Prośba o kukurydzę:''' Jeśli masz 2 lub mniej koszy kukurydzy, można poprosić o kukurydzę, chyba że jesteś na dole wszystkich trzech świątyń. (Odrzuć całą kukurydzę i weź 3 kosze kukurydzy i przejdź o jeden stopień w dół jednej ze świątyń) - '''Wyślij do pracy na kołach dowolną liczbę robotników lub zdejmij dowolną liczbę robotników z kół''' -- ''Wysyłanie robotników do pracy'': Gracz płaci tyle koszów kukurydzy, ile wynosi numer pola akcji na które wysłał robotnika. Płaci się również dodatkowe kosze kukurydzy za każdego dodatkowego robotnika wysłanego w danej turze -- ''Powrót robotników do wioski'': Można wybrać jednego lub więcej z twoich umieszczonych robotników. Dzięki każdemu powracającemu robotnikowi gracz może: --- Wykonać akcję przy której stał robotnik w momencie zdjęcia, --- Wykonać akcję z jednego z pól o niższym numerze na tym samym kole. Za każdy numer różnicy, gracz musi zapłacić 1 kosz kukurydzy, --- Nie robić nic. Gracz zdejmuje tylko robotnika z koła. == '''Dzień Żywienia''' == -'''Żywienie Robotników''' Każdy gracz musi zapłacić 2 kosze kukurydzy za każdego robotnika, którego ma w grze (na kole lub przed sobą. Gracz musi nakarmić, tylu robotników ile jest w stanie. - Za każdego nienakarmionego robotnika, traci 3 punkty zwycięstwa -'''Weź nagrody''' --''W środku epoki (Brązowo-pomarańczowe Dni Żywienia)'' --- Gracze otrzymują nagrody w postaci surowców i kryształowych czaszek, -- ''Koniec epoki (Niebiesko-zielone Dni Żywienia)'' --- Usuń wszystkie budynki epoki 1 i wyłóżcie budynki epoki 2. --- Gracz otrzymuje punkty zwycięstwa wskazane przez aktualnie zajmowany stopień w świątyni. --- Gracz znajdujący się w danej świątyni na najwyższym stopniu spośród wszystkich graczy otrzymuje punkty bonusowe. Określają go liczby widniejące nad każdą świątynią. W przypadku remisu, wszyscy remisujący gracze otrzymują połowę premii. == '''Przestawianie kalendarza''' == Jeśli nie ma robotnika na polu Pierwszeństwa żadnego gracza, należy umieścić 1 kosz kukurydzy na aktualnym zębie kalendarza Tzolk'in i przesunąć go o 1 dzień. W przeciwnym razie: - Robotnik znajdujący się na najwyżej numerowanym polu wraca do gracza -- Jeśli tą osobą nie był Pierwszy Gracz, bierze znacznik Pierwszego Gracza. -- Jeśli gracz zajmujący pole Pierwszeństwa posiada aktualnie znacznik Pierwszego Gracza, przekazuje go graczowi po swojej lewej stronie. - Przestawić kalendarz Tzolk'in o jeden dzień. - Gracz, który umieścił swojego robotnika na polu Pierwszeństwa może zdecydować, aby przesunąć kalendarz o jeden dodatkowy dzień, jeśli jego plansza nie jest obrócona ciemną stroną ku górze i w rezultacie jakiś robotnik nie zostałby zepchnięty z koła (a nie byłby zepchnięty przy obrocie kalendarza o 1 dzień). -- W przypadku skorzystania z tej opcji, należy obrócić swoją planszę ciemną stroną w górę. -- Przesunięcie kalendarza o 2 dni nie powoduje ominięcia Dnia Żywienia. 8021b92dcc87698161db6689bd4b6f24d3444c02 923 922 2013-08-10T18:31:57Z Januszk0 3262 Undo revision 922 by [[Special:Contributions/Januszk0|Januszk0]] ([[User talk:Januszk0|talk]]) wikitext text/x-wiki == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of themples from step you are on and all steps below -- ''End of Age (Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option, flip your board to dark side up. -- This does not avoid food days. 492a51ebdd36dc9ecb3fa747a046c5a54b18ca80 Gamehelpjaipur 0 115 928 792 2013-09-03T06:18:06Z Hofty 3828 wikitext text/x-wiki In Jaipur, each player has a hand of cards with various (color-coded) goods on them. Each player also has a stack of camel cards (their "herd") which is not considered part of their hand. There are five cards face-up between the players that are available for the players to take. On your turn, you take one of three actions: # '''Take''' a single card or all the camels in the face up row. #: You may take any single goods card from the card row and add it to your hand, or take all the camels in the row and add them to your herd. #: You are limited to having 7 goods cards in your hand, so you may not take a goods card if you already have 7 goods cards in your hand. (Again, your herd does not count toward this limit.) #: The card row is then replenished from the deck so it always has 5 cards at the start of a turn. # '''Trade''' for 2 or more goods cards from the row. #: You may trade goods cards from your hand or camels from your herd for goods cards from the card row. For every one card you trade from your hand or herd, you may pick up one goods card from the row and add it to your hand. #: The cards your traded are added to the card row, so it will still have 5 cards. # '''Sell''' cards to earn points. #: You may sell one or more cards to earn points, but all the cards you sell must be the same color. #: If you are selling Diamonds, Gold, or Silver (the red, yellow, and gray cards) you MUST sell at least two cards. #: Regardless of the type of good, each card you sell lets you take one token of the corresponding color from the bank, starting from the most valuable token. #: If you sell 3 or more goods, you also earn a bonus token from the corresponding pile (for sales of 3,4, or 5+ goods). #: The sold goods cards are discarded. The game continues until either: * Three piles of goods tokens are depleted; OR * The card row cannot be replenished. : At that point, players total the points they have on their earned goods tokens and bonus tokens. : In addition, whichever player has more camels in their herd at the end of the round earns the camel bonus token, worth 5 points. : Whichever player earns more points total wins the round. : Tie-breaker, in order: :* Player with more bonus tokens wins the round; :* Player with more goods tokens wins the round; After the round, the game resets and new hands are dealt. The first player to win two rounds wins the game. e52209133c4cf5f83fe7b582a15608cf0196cdbe How to join BGA developer team? 0 83 929 777 2013-09-30T15:58:44Z Sourisdudesert 1 wikitext text/x-wiki Registering on BGA Studio is simple and automatic from: http://en.studio.boardgamearena.com To register, you must agree with [http://en.doc.boardgamearena.com/images/0/02/BGA_TC_Dev_en.pdf ''''terms & conditions' document''']. It's very light, so as to get to the fun part faster. Once registered, you will get by email * one login / password to access files through SFTP * one login / password to access the database (for your games in progress) * ten logins with numeric suffixes from 0 to 9 and a common simple password to test games on the studio website while developing. == Ok, I registered, how to start? == Then... well that's all, you can start! See [[Studio#Great.2C_I.27m_in.21_..._How_should_I_start.3F|Great, I'm in! ... How should I start?]] fb343543e19b566d00dc6f09ee976e3b6833157c Studio 0 49 930 892 2013-09-30T16:00:09Z Sourisdudesert 1 /* What is Board Game Arena Studio? */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) BGA Studio website: http://en.studio.boardgamearena.com (original announcement on BGA forum: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973) == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio logs]] * [[Studio back-office]] * [[Studio FAQ]] == BGA Developer team organization == * [[Steps to create a BGA game]] * [[Post-release phase]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] c35e1c0c136bab1179c58e3975449dc99f1e54fe 951 930 2013-09-30T16:39:22Z Sourisdudesert 1 /* Other components */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) BGA Studio website: http://en.studio.boardgamearena.com (original announcement on BGA forum: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973) == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game meta-information: gameinfos.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio logs]] * [[Studio back-office]] * [[Studio FAQ]] == BGA Developer team organization == * [[Steps to create a BGA game]] * [[Post-release phase]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] b535d7773a8c8fd1bca0347173a6b7d9be556f76 First steps with BGA Studio 0 111 931 775 2013-09-30T16:01:31Z Sourisdudesert 1 /* Connect to your SFTP folder and to the BGA Studio website */ wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as [http://winscp.net/ WinSCP] for example) # Check that your home folder contains : #* One folder for each of the three example games (reversi, hearts, gomoku) #* One folder matching the game you will be developing #* One 'resources.html' file # Open the resources.html file, it contains : #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make a change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do so. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specificites of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 586dd4594ea28af9e205f5669a05e5e8aed64c46 932 931 2013-09-30T16:04:24Z Sourisdudesert 1 /* Connect to your SFTP folder and to the BGA Studio website */ wikitext text/x-wiki == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as [http://winscp.net/ WinSCP] for example) # Check that your home folder contains one folder for each of the three example games (reversi, hearts, gomoku). Each time you create a new game project, one additional folder is added to your "home" folder. # Open the resources.html file, it contains : #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make a change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do so. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specificites of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. af565310fc4858efe51b38ad1c807020ce5aae1a 933 932 2013-09-30T16:08:02Z Sourisdudesert 1 wikitext text/x-wiki == Connect to the BGA Studio website == Go to BGA Studio website: http://en.studio.boardgamearena.com Choose one of your 10 accounts (ex: myusername0), and login into the website - as you would do for Board Game Arena. == Check projects in progress and available game licences == On the left menu, you can check game projects under development and available game licences. From this point, you can choose what you want to do: * Join an existing project. * Create a new project. * Try to get a licence for a game you don't see in the "available game licences" page (see how-to on "available licences" page). == Connect to your SFTP folder and to the BGA Studio website == After your account has been created, you will get by email: * the name of the SFTP server to connect to * your SFTP login and password * ten BGA Studio logins ending with a numeral ranging from 0 to 9 and the password needed to use them. Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as [http://winscp.net/ WinSCP] for example) # Check that your home folder contains one folder for each of the three example games (reversi, hearts, gomoku). Each time you create a new game project, one additional folder is added to your "home" folder. # Open the resources.html file, it contains : #* The URL pointing to the BGA Studio website (protected with an HTTP Basic authentication scheme, the login and password are also referenced in the 'resources.html' file #* The URL pointing to the BGA Studio backoffice (please note that you must first be logged in on the BGA Studio website to be able to access the backoffice, as the authentication is shared between the two) #* The URL pointing to the web administration tool for the BGA Studio database. # Click on the URL to the BGA Studio website, enter the HTTP Basic credentials when prompted for them (and have your browser conveniently memorize them for you). Then you get to an home page just like the one of the main BGA website. Enter one of your ten BGA studio logins to connect as a user. == Launch your game and check how to update it == # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. Here is how to go through your first commit: # Go back to your browser tab showing the 'resources.html' file. Click on the URL pointing to the BGA Studio backoffice (do not log out of the BGA Studio website before doing so, or you will get a '''Not authorized''' error message as the authentication is shared); # Click on the 'Sources' menu entry to show the commit form. Enter your game name (under the same form as the name of your game folder: lowercase, no spaces), enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. NB: you should also commit each time you make a change to your gameoptions.inc.php file or to your stats.inc.php file, as an extra deployment action is needed from us for these files to take effect. Please mention in your commit comment that you need us to deploy those files, or send us an email to ask us to do so. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specificites of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 4db9f5edfea9064f7f121c960821ffa45437f95e 934 933 2013-09-30T16:15:35Z Sourisdudesert 1 wikitext text/x-wiki == Connect to the BGA Studio website == Go to BGA Studio website: http://en.studio.boardgamearena.com Choose one of your 10 accounts (ex: myusername0), and login into the website - as you would do for Board Game Arena. == Check projects in progress and available game licences == On the left menu, you can check game projects under development and available game licences. From this point, you can choose what you want to do: * Join an existing project. * Create a new project. * Try to get a licence for a game you don't see in the "available game licences" page (see how-to on "available licences" page). == Create a new game project == You can do most of projects-related operation from "Control Panel / Manage games". In particular, you can create a new project automatically from there. == Connect to your SFTP folder == From the initial email from the Studion you get: * the name of the SFTP server to connect to * your SFTP login and password Using this information: # Connect to the SFTP server using your SFTP login and password, through your favourite SFTP client software (such as [http://winscp.net/ WinSCP] for example) # Check that your home folder contains one folder for each of the three example games (reversi, hearts, gomoku). If you have already created a new game project, one additional folder should be in your "home" folder. == Let's code! == Now, you can try to launch a new game on BGA Studio from the "Play now" menu entry, as you would do on Board Game Arena website. # Find your game in the 'Play now' section and launch a table # Use the 'I want between X and X' players to tick down the maximum players number to the minimum # Click 'Express start': your game launches with the maximum number of players specified. It shows an empty canvas: in the game zone you just have a sentence 'This is your game interface. You can edit this HTML in your ".tpl" file.'. # Switch to your SFTP home folder, go into your game folder. Edit the game_game.tpl file, and change this sentence to 'Hey, this is my first game!', then save. # Go back to your browser and refresh, check that the game zone has updated. # Click on the 'Exit game' icon on the top right, and in the popup choose 'Express game stop'. The game ends automatically and you are brought back to the table screen for this ended game. # Switch to your game folder, go into the img folder and overwrite your game_box.png file with another image. # Go back to your browser, '''empty your browser cache''', then refresh the page, and check that the game box image has been updated. Then you can modify the provided skeleton and begin to develop your game :) == Commit your changes == Committing uploads your changes on our [http://en.wikipedia.org/wiki/Revision_control revision control] system. This is an extra assurance not to lose your code, and to have the possibility to get a previous version of your code if you need to backtrack. It also helps us to follow your progress (we get an email when you commit). So you should commit from time to time, when you hit some landmark in your development. You can automatically commit your sources in the repository from "Control Panel / Manage Games / Your games / Commit my modifications". Then: # Enter your commit comment (such as 'My first commit') then hit the 'Submit' button; # Check the log for errors, it should end with the following lines: Transmitting file data . Committed revision #revision number#. HAL says: done. == That's all! == Now you know about the basics of updating your game on BGA Studio and testing your changes. For more information on the specificites of each file, please check out the [[Studio#BGA_Studio_documentation | reference documentation for the framework]]. 84374110062c7881c18ce66a4fc7739285e46ba1 Tutorial reversi 0 57 935 759 2013-09-30T16:16:56Z Sourisdudesert 1 /* Let it look like Reversi */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('../../img/reversi/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "this.addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "states.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 5bf435974f89c8abeb2713fa66580b3e0b38160f 936 935 2013-09-30T16:17:31Z Sourisdudesert 1 /* Make the squares appears */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('../../img/reversi/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "this.addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "states.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 282982cfad91e05e90904077f85b206b8d802497 937 936 2013-09-30T16:17:49Z Sourisdudesert 1 /* The discs */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for width and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('img/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "this.addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "states.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". aa698a8b78cfb8f1505e1f220027722caa717164 Tutorial gomoku 0 73 938 709 2013-09-30T16:19:55Z Sourisdudesert 1 /* Setup the board */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( 'img/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { // At the end of the slide, update the intersection dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'no_stone' ); dojo.addClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'stone_' + notif.args.color ); dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); // We can now destroy the stone since it is now visible through the change in style of the intersection dojo.destroy( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ); })); slide.play(); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( '../../img/gomoku/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, send a score update notification to the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] e99b68b52465537142e09fa28207afcf7ee2c1fb 939 938 2013-09-30T16:21:42Z Sourisdudesert 1 /* Manage states and events */ wikitext text/x-wiki This tutorial will guide you through the basics of creating a simple game on BGA Studio, through the example of [http://en.wikipedia.org/wiki/Gomoku '''Gomoku'''] (also known as Gobang or Five in a Row). == You will start from our 'emtpy game' template == Here is how your games looks by default when it has just been created: [[File:Gomoku tuto1.png]] == Setup the board == Gather useful images for the game and edit them as needed. Upload them in the 'img' folder of your SFTP access. Edit .tpl to add some divs for the board in the HTML. For example: <pre> <div id="gmk_game_area"> <div id="gmk_background"> <div id="gmk_goban"> </div> </div> </div> </pre> Edit .css to set the div sizes and positions and show the image of the board as background. <pre> #gmk_game_area { text-align: center; position: relative; } #gmk_background { width: 620px; height: 620px; position: relative; display: inline-block; } #gmk_goban { background-image: url( 'img/goban.jpg'); width: 620px; height: 620px; position: absolute; } </pre> [[File:Gomoku tuto2.png]] == Setup the backbone of your game == Edit dbmodel.sql to create a table for intersections. We need coordinates for each intersection and a field to store the color of the stone on this intersection (if any). <pre> CREATE TABLE IF NOT EXISTS `intersection` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `coord_x` tinyint(2) unsigned NOT NULL, `coord_y` tinyint(2) unsigned NOT NULL, `stone_color` varchar(8) NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 AUTO_INCREMENT=1 ; </pre> Edit .game.php->setupNewGame() to insert the empty intersections (19x19) with coordinates into the database. <pre> // Insert (empty) intersections into database $sql = "INSERT INTO intersection (coord_x, coord_y) VALUES "; $values = array(); for ($x = 0; $x < 19; $x++) { for ($y = 0; $y < 19; $y++) { $values[] = "($x, $y)"; } } $sql .= implode( $values, ',' ); self::DbQuery( $sql ); </pre> Edit .game.php->getAllDatas() to retrieve the state of the intersections from the database. <pre> // Intersections $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection "; $result['intersections'] = self::getCollectionFromDb( $sql ); </pre> Edit .tpl to create a template for intersections. <pre> var jstpl_intersection='<div class="gmk_intersection ${stone_type}" id="intersection_${x}_${y}"></div>'; </pre> Define the styles for the intersection divs. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; } </pre> Edit .js->setup() to setup the intersections layer that will be used to get click events and to display the stones. The data you returned in $result['intersections'] in .game.php->getAllDatas() is now available in your .js->setup() in gamedatas.intersections. <pre> // Setup intersections for( var id in gamedatas.intersections ) { var intersection = gamedatas.intersections[id]; dojo.place( this.format_block('jstpl_intersection', { x:intersection.coord_x, y:intersection.coord_y, stone_type:(intersection.stone_color == null ? "no_stone" : 'stone_' + intersection.stone_color) } ), $ ( 'gmk_background' ) ); var x_pix = this.getXPixelCoordinates(intersection.coord_x); var y_pix = this.getYPixelCoordinates(intersection.coord_y); this.slideToObjectPos( $('intersection_'+intersection.coord_x+'_'+intersection.coord_y), $('gmk_background'), x_pix, y_pix, 10 ).play(); if (intersection.stone_color != null) { // This intersection is taken, it shouldn't appear as clickable anymore dojo.removeClass( 'intersection_' + intersection.coord_x + '_' + intersection.coord_y, 'clickable' ); } } </pre> Use some temporary css border-color or background-color and opacity to see the divs and make sure you have them positioned right. <pre> .gmk_intersection { width: 30px; height: 30px; position: relative; background-color: blue; opacity: 0.3; } </pre> You can declare some constants in material.inc.php and pass them to your .js for easy repositioning (modify constant, refresh). This is especially useful if the same constants have to be used on the server and on the client. * Declare your constants in material.inc.php (this will be automatically included in your .game.php) <pre> $this->gameConstants = array( "INTERSECTION_WIDTH" => 30, "INTERSECTION_HEIGHT" => 30, "INTERSECTION_X_SPACER" => 2.8, // Float "INTERSECTION_Y_SPACER" => 2.8, // Float "X_ORIGIN" => 0, "Y_ORIGIN" => 0, ); </pre> * In .game.php->getAllDatas(), add the constants to the result array // Constants $result['constants'] = $this->gameConstants; * In .js constructor, define a class variable for constants // Game constants this.gameConstants = null; * In .js->setup() assign the constants to this variable this.gameConstants = gamedatas.constants; * Then use it in your getXPixelCoordinates and getYPixelCoordinates functions getXPixelCoordinates: function( intersection_x ) { return this.gameConstants['X_ORIGIN'] + intersection_x * (this.gameConstants['INTERSECTION_WIDTH'] + this.gameConstants['INTERSECTION_X_SPACER']); }, getYPixelCoordinates: function( intersection_y ) { return this.gameConstants['Y_ORIGIN'] + intersection_y * (this.gameConstants['INTERSECTION_HEIGHT'] + this.gameConstants['INTERSECTION_Y_SPACER']); }, Here is what you should get: [[File:Gomoku tuto3.png]] == Manage states and events == Define your game states in states.inc.php. For gomoku we will use 3 states in addition of the predefined states 1 (gameSetup) and 99 (gameEnd). One to play, one to check the end game condition, one to give his turn to the other player if the game is not over. The first state requires an action from the player, so its type is 'activeplayer'. The two others are automatic actions for the game, so their type is 'game'. We will update the progression while checking for the end of the game, so for this state we set the 'updateGameProgression' flag to true. <pre> 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a stone'), "descriptionmyturn" => clienttranslate('${you} must play a stone'), "type" => "activeplayer", "possibleactions" => array( "playStone" ), "transitions" => array( "stonePlayed" => 3, "zombiePass" => 3 ) ), 3 => array( "name" => "checkEndOfGame", "description" => '', "type" => "game", "action" => "stCheckEndOfGame", "updateGameProgression" => true, "transitions" => array( "gameEnded" => 99, "notEndedYet" => 4 ) ), 4 => array( "name" => "nextPlayer", "description" => '', "type" => "game", "action" => "stNextPlayer", "transitions" => array( "" => 2 ) ), </pre> Implement the 'stNextPlayer()' function in .game.php to manage turn rotation. Except if there are special rules for the game turn depending on context, this is really easy: <pre> function stNextPlayer() { self::trace( "stNextPlayer" ); // Go to next player $active_player = self::activeNextPlayer(); self::giveExtraTime( $active_player ); $this->gamestate->nextState(); } </pre> Add onclick events on intersections in .js->setup() // Add events on active elements (the third parameter is the method that will be called when the event defined by the second parameter happens - this method must be declared beforehand) this.addEventToClass( "gmk_intersection", "onclick", "onClickIntersection"); Declare the corresponding .js->onClickIntersection() function, which calls an action function on the server with appropriate parameters <pre> onClickIntersection: function( evt ) { console.log( '$$$$ Event : onClickIntersection' ); dojo.stopEvent( evt ); if( ! this.checkAction( 'playStone' ) ) { return; } var node = evt.currentTarget.id; var coord_x = node.split('_')[1]; var coord_y = node.split('_')[2]; console.log( '$$$$ Selected intersection : (' + coord_x + ', ' + coord_y + ')' ); if ( this.isCurrentPlayerActive() ) { this.ajaxcall( "/gomoku/gomoku/playStone.html", { lock: true, coord_x: coord_x, coord_y: coord_y }, this, function( result ) {}, function( is_error ) {} ); } }, </pre> Add this action function in .action.php, retrieving parameters and calling the appropriate game action <pre> public function playStone() { self::setAjaxMode(); // Retrieve arguments // Note: these arguments correspond to what has been sent through the javascript "ajaxcall" method $coord_x = self::getArg( "coord_x", AT_posint, true ); $coord_y = self::getArg( "coord_y", AT_posint, true ); // Then, call the appropriate method in your game logic, like "playCard" or "myAction" $this->game->playStone( $coord_x, $coord_y ); self::ajaxResponse( ); } </pre> Add game action in .game.php to update the database, send a notification to the client providing the event notified (‘stonePlayed’) and its parameters, and proceed to the next state. <pre> function playStone( $coord_x, $coord_y ) { // Check that this is player's turn and that it is a "possible action" at this game state (see states.inc.php) self::checkAction( 'playStone' ); $player_id = self::getActivePlayerId(); // Check that this intersection is free $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE coord_x = $coord_x AND coord_y = $coord_y AND stone_color is null "; $intersection = self::getObjectFromDb( $sql ); if ($intersection == null) { throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); } // Get player color $sql = "SELECT player_id, player_color FROM player WHERE player_id = $player_id "; $player = self::getNonEmptyObjectFromDb( $sql ); $color = ($player['player_color'] == 'ffffff' ? 'white' : 'black'); // Update the intersection with a stone of the appropriate color $intersection_id = $intersection['id']; $sql = "UPDATE intersection SET stone_color = '$color' WHERE id = $intersection_id "; self::DbQuery($sql); // Notify all players self::notifyAllPlayers( "stonePlayed", clienttranslate( '${player_name} dropped a stone ${coordinates}' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'coordinates' => $this->getFormattedCoordinates($coord_x, $coord_y), 'coord_x' => $coord_x, 'coord_y' => $coord_y, 'color' => $color ) ); // Go to next game state $this->gamestate->nextState( "stonePlayed" ); } </pre> Catch the notification in .js->setupNotifications() and link it to a javascript function to execute when the notification is received. <pre> setupNotifications: function() { console.log( 'notifications subscriptions setup' ); dojo.subscribe( 'stonePlayed', this, "notif_stonePlayed" ); } </pre> Implement this function in javascript to update the intersection to show the stone, and register it inside the setNotifications function. <pre> notif_stonePlayed: function( notif ) { console.log( '**** Notification : stonePlayed' ); console.log( notif ); // Create a stone dojo.place( this.format_block('jstpl_stone', { stone_type:'stone_' + notif.args.color, x:notif.args.coord_x, y:notif.args.coord_y } ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ) ); // Place it on the player panel this.placeOnObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'player_board_' + notif.args.player_id ) ); // Animate a slide from the player panel to the intersection dojo.style( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y, 'zIndex', 1 ); var slide = this.slideToObject( $( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ), $( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y ), 1000 ); dojo.connect( slide, 'onEnd', this, dojo.hitch( this, function() { // At the end of the slide, update the intersection dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'no_stone' ); dojo.addClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'stone_' + notif.args.color ); dojo.removeClass( 'intersection_' + notif.args.coord_x + '_' + notif.args.coord_y, 'clickable' ); // We can now destroy the stone since it is now visible through the change in style of the intersection dojo.destroy( 'stone_' + notif.args.coord_x + '_' + notif.args.coord_y ); })); slide.play(); }, </pre> For this function to work properly, you also need: * to declare a stone javascript template in your .tpl file. <pre> var jstpl_stone='<div class="gmk_stone ${stone_type}" id="stone_${x}_${y}"></div>'; </pre> * to define the css styles for the stones <pre> .gmk_stone { width: 30px; height: 30px; position: absolute; background-image: url( 'img/stones.png'); } .no_stone { background-position: -60px 0px; } .stone_black { background-position: 0px 0px; } .stone_white { background-position: -30px 0px; } </pre> These styles rely on an PNG image (with transparent background) of both the white and black stones, and positions the background appropriately to show only the part of the background image matching the appropriate stone (or the transparent space if there is no stone). Here is what the image looks like: [[File:Gomoku stones.png]] The red circle is used to highlight intersections where you can drop a stone when the player's cursor hovers over them (we also change the cursor to a hand). To do this: * we define in the css file the 'clickable' css class <pre> .clickable { cursor: pointer; } .clickable:hover { background-position: -90px 0px; } </pre> * in .js, when we enter the 'playerTurn' state, we add the 'clickable' style to the intersections where there is no stone <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': if( this.isCurrentPlayerActive() ) { var queueEntries = dojo.query( '.no_stone' ); for(var i=0; i<queueEntries.length; i++) { dojo.addClass( queueEntries[i], 'clickable' ); } } } }, </pre> The basic game turn is implemented: you can now drop some stones! [[File:Gomoku tuto4.png]] == Cleanup your styles == Remove temporary css visualisation helpers : looks good! [[File:Gomoku tuto5.png]] == Implement rules and end of game conditions == Implement specific rules for the game. For example in Gomoku, black plays first. So in .game.php->setupNewGame(): * modify the default colors for players to white and black $default_colors = array( "000000", "ffffff", ); * and at the end of the setup make the black player active <pre> // Black plays first $sql = "SELECT player_id, player_name FROM player WHERE player_color = '000000' "; $black_player = self::getNonEmptyObjectFromDb( $sql ); $this->gamestate->changeActivePlayer( $black_player['player_id'] ); </pre> Implement rule for computing game progression in .game.php->getGameProgression(). For Gomoku we will use the rate of occupied intersections over the total number of intersections. This will often be wildly inaccurate as the game can end pretty quickly, but it's about the best we can do (the game can drag to a stalemate with all intersections occupied and no winner). <pre> function getGameProgression() { // Compute and return the game progression // Number of stones laid down on the goban over the total number of intersections * 100 $sql = " SELECT round(100 * count(id) / (19*19) ) as value from intersection WHERE stone_color is not null "; $counter = self::getNonEmptyObjectFromDB( $sql ); return $counter['value']; } </pre> Implement end of game detection and update the score according to who is the winner. It is easier to check for a win directly after setting the stone, so: * declare a global 'end_of_game' variable in .game.php->Gomoku() self::initGameStateLabels( array( "end_of_game" => 10, ) ); * init that global variable to 0 in .game.php->setupNewGame() self::setGameStateInitialValue( 'end_of_game', 0 ); * add the appropriate code in .game.php before proceeding to the next state, using a checkForWin() function implemented separately for clarity. If the game has been won, we set the score, send a score update notification to the client side, and set the 'end_of_game' global variable to 1 as a flag signaling that the game has ended. <pre> // Check if end of game has been met if ($this->checkForWin( $coord_x, $coord_y, $color )) { // Set active player score to 1 (he is the winner) $sql = "UPDATE player SET player_score = 1 WHERE player_id = $player_id"; self::DbQuery($sql); // Notify final score $this->notifyAllPlayers( "finalScore", clienttranslate( '${player_name} wins the game!' ), array( "player_name" => self::getActivePlayerName(), "player_id" => $player_id, "score_delta" => 1, ) ); // Set global variable flag to pass on the information that the game has ended self::setGameStateValue('end_of_game', 1); // End of game message $this->notifyAllPlayers( "message", clienttranslate('Thanks for playing!'), array( ) ); } </pre> * Then in the gomoku->stCheckEndOfGame() function which is called when your state machine goes to the 'checkEndOfGame' state, check for this variable and for other possible 'end of game' conditions (draw). <pre> function stCheckEndOfGame() { self::trace( "stCheckEndOfGame" ); $transition = "notEndedYet"; // If there is no more free intersections, the game ends $sql = "SELECT id, coord_x, coord_y, stone_color FROM intersection WHERE stone_color is null"; $free = self::getCollectionFromDb( $sql ); if (count($free) == 0) { $transition = "gameEnded"; } // If the 'end of game' flag has been set, end the game if (self::getGameStateValue('end_of_game') == 1) { $transition = "gameEnded"; } $this->gamestate->nextState( $transition ); } </pre> * Catch the score notification on the client side in .js->setupNotifications(). It is advised to set up a small delay after that so that end of game popup doesn't show too quickly. <pre> dojo.subscribe( 'finalScore', this, "notif_finalScore" ); this.notifqueue.setSynchronous( 'finalScore', 1500 ); </pre> * Implement the function declared to handle the notification. <pre> notif_finalScore: function( notif ) { console.log( '**** Notification : finalScore' ); console.log( notif ); // Update score this.scoreCtrl[ notif.args.player_id ].incValue( notif.args.score_delta ); }, </pre> '''Test everything thoroughly... you are done!''' [[File:Gomoku tuto6.png]] 5b632d978467043e546b8e53050a67a7c86201ad Studio FAQ 0 53 940 831 2013-09-30T16:22:43Z Sourisdudesert 1 /* I can't access the Studio back-office, I get a 'Not authorized' error message? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == I don't know the name to use to commit my game, what name should I use? == The game name for committing is the name of the game in lower case and without spaces or special characters (ex: puertorico). It is the same name as the name used for the game folder in your SFTP access. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == When you modify these 2 files, you need to deploy the update from your BGA backoffice page. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 4f4bdecc8aefcd03cb2a55b3275da02813136c01 941 940 2013-09-30T16:22:51Z Sourisdudesert 1 /* I don't know the name to use to commit my game, what name should I use? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == BGA administrators will translate the game in French before the game release. After the game release, the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == When you modify these 2 files, you need to deploy the update from your BGA backoffice page. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 9702d3fd8ae2fc57912a7b38c8fb3fb1f582d33d 942 941 2013-09-30T16:23:26Z Sourisdudesert 1 /* How can I provide translation in my language? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == The community will translate the game in all language after the game release with the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == On BGA Studio, there is the gameserver you are developing on, and the main site server that is used to launch the games. The game icon, box and the publisher logo are hosted on the main site server, so they are not immediately available when you modify them on the gameserver. To deploy them on the main site, you have to use the [[Studio back-office]] to do a commit. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == When you modify these 2 files, you need to deploy the update from your BGA backoffice page. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 8ebb6ee7fdf24f8caa59262dab1786dcc53fcf91 943 942 2013-09-30T16:25:25Z Sourisdudesert 1 /* I updated the images in the 'img' folder of my game, but they don't show? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == The community will translate the game in all language after the game release with the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == '''Concerning game_box.png, game_icon.png and publisher.png''': You must use "Reload game box image" function from "Control Panel / Manage Games / Your game" to make your change visible. '''Concerning other images''': Other images used during the game are immediately available without any action. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics, but they don't show? == When you modify these 2 files, you need to deploy the update from your BGA backoffice page. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 0866a26dcc172d82d512489bf6e1883e7573515e 944 943 2013-09-30T16:26:51Z Sourisdudesert 1 /* I added some game options / some game statistics, but they don't show? */ wikitext text/x-wiki This is a place where we will collect and answer frequently asked questions. == What should I use to access the files through SFTP? == There is a lot of tools to do that. Use the one you are the most comfortable with. On Linux Gnome, you can for example use the 'Connect to server' function of the Nautilus file management system, or use sshfs. On Windows, there is for example the [http://winscp.net/ WinSCP client]. == I can't edit the files in my game directory, it looks like they are readonly. What's happening? == Maybe there is a maintenance operation underway. If you don't get access back after some time (say one or two hours), please send us a mail to check. == What is the working language on BGA studio? == Working language is '''English'''. Variables and functions must be named with English words. Comments must be written in English. Game interface strings and game logs must be written in English. == How can I provide translation in my language? == The community will translate the game in all language after the game release with the collaborative translation interface can be used to translate into other languages. Check [[Translations]] to see how to make your game translatable. == I updated the images in the 'img' folder of my game, but they don't show? == '''Concerning game_box.png, game_icon.png and publisher.png''': You must use "Reload game box image" function from "Control Panel / Manage Games / Your game" to make your change visible. '''Concerning other images''': Other images used during the game are immediately available without any action. If the images still don't show after that, please try emptying your browser cache and reloading the page. == I added some game options / some game statistics / some game infos but they don't show? == When you modify one of these 3 files, you need to use the corresponding update action from "Control Panel / Manage Games / Your Game" in order we can take this information into account. == Should I use images free from copyright? == If you are developing a game in the public domain, yes (or you can make your own if you feel it's better). If you are developing a game for which we have a licence, we will usually provide art files from the publisher. 204f89bb29ebe846f92df7fb2465c3fbc5525394 Studio file reference 0 54 945 454 2013-09-30T16:29:24Z Sourisdudesert 1 wikitext text/x-wiki This is a quick reference for the files used to implement a game. For more information, edit the file and read the introductory comment. === 'img' directory === This directory contains the images for your game (the game art). === gameinfos.inc.php === In this file you describe the meta-information for your game: game name, publisher name, number of player, game categories, etc... === dbmodel.sql === File for creating specific database tables that you will need to persist data during the game (for example a table for cards). === gameoptions.inc.php === File for describing your game options (= game variants) === <gamename>.action.php === File used to describe methods that can be called from the client interface through javascript, get parameters and call the appropriate game functions. === <gamename>.css === CSS styles specific to your game. === <gamename>.game.php === This is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. === <gamename>.js === This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. === <gamename>.view.php and <gamename>_<gamename>.tpl === Files used to set up the page layout ('view') for the game. === material.inc.php === File used to describe all the game material (cards with their description, dices, tokens...). You can also use it to define game constants. === states.inc.php === This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). === stats.inc.php === File used to list statistics that you want to update during the game to be presented to players at the end of the game. 16fa2ec038a841941fbc7955f6cd2d40702d2ba7 Main game logic: yourgamename.game.php 0 86 946 837 2013-09-30T16:32:23Z Sourisdudesert 1 /* Manage player scores and Tie breaker */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your gameinfos.inc.php file like this: <pre> 'tie_breaker_description' => totranslate("Describe here your tie breaker formula"), </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaVisibleSystemException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaVisibleSystemException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. c7a1c116b0b88b77bab55c2bf6aa0b5fd6243a66 Game interface stylesheet: yourgamename.css 0 96 947 832 2013-09-30T16:33:26Z Sourisdudesert 1 wikitext text/x-wiki This is the CSS stylesheet of your game User Interface. Styles defined on this file will be applied to the HTML elements you define in your HTML template (yourgame_yourgame.tpl), and to HTML elements you create dynamically with Javascript. Usually, you are using CSS to: 1°) define the overall layout of your game (ex: place the board on the top left, place player's hand beside, place the deck on the right, ...). 2°) create your CSS-sprites: All images of your games should be gathered into a small number of image files. Then, using background-image and background-position CSS properties, you create HTML blocks that can display these images correctly. Example: <pre> Example of CSS sprites (a black token and a white token, 20x20px each, embedded in the same "tokens.png" 40x20px image): .white_token { background-image: url('img/tokens.png'); background-position: 0px 0px; } .black_token { background-image: url('img/tokens.png'); background-position: -20px 0px; } .token { width: 20px; height: 20px; background-repeat: none; } </pre> 3°) ... anything else: It is really easy to add and remove CSS classes dynamically from your Javascript with dojo.addClass and dojo.removeClass. It is also easy to check if an element has a class (dojo.hasClass) or to get all elements with a specific class (dojo.query). This is why, very often, using CSS classes for the logic of your user interface allow you to do complex thing easily. Note: on the production platform, this file will be compressed and comments will be removed. Consequently, don't hesitate to put as many comments as necessary. Important: ALL the CSS directives for your game must be included in this CSS file. You can't create additional CSS files and import them. == Warning: using Z-index == You may use z-index CSS property in your game interface, but you should pay attention to the following: BGA dialogs are displayed with a z-index of 950. If you want to use z-index safely, you should use value '''lower than 900'''. About z-index: don't forget that if you are using a z-index, your element will be displayed above all elements that do not have a z-index. So it's no use to have big z-index values: 1 is enough most of the time :) == spectatorMode == When a spectator (= a player that is not part of the game) is viewing a game, the BGA framework add the CSS class "spectatorMode" to the wrapping HTML tag of your game. This way, if you want to apply a special style to some elements of your game for spectators, you can do this in your CSS: <pre> .spectatorMode #your_element_id { /* your special style */ } </pre> The most common usage of this is to hide some elements to spectators. For example, to hide "my hand" elements: <pre> .spectatorMode #my_hand { display: none; } </pre> d2ecdabf8bbe79bc9de1af33a714ff292cf0cc2d Game art: img directory 0 89 948 458 2013-09-30T16:35:56Z Sourisdudesert 1 /* Images loading */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. ;publisher2.png (optional) * If the game has been co-published by 2 publishers, you should upload a second image named "publisher2.png" (same characteristic than the first one). == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript method "dontPreloadImage" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] c375cbd06f058de2b6a7055ff948b6cc493d0705 949 948 2013-09-30T16:37:28Z Sourisdudesert 1 /* Requested images */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. ;publisher2.png (optional) * If the game has been co-published by 2 publishers, you should upload a second image named "publisher2.png" (same characteristic than the first one). '''Important''': when you modify these images, you MUST click on "Reload game box image" from the Control Panel in order your update can be taken into account. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript method "dontPreloadImage" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] 63dfdacfb94b2035891ea1876834814a7accb371 950 949 2013-09-30T16:37:41Z Sourisdudesert 1 wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be an image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. ;publisher2.png (optional) * If the game has been co-published by 2 publishers, you should upload a second image named "publisher2.png" (same characteristic than the first one). '''Important''': when you modify these images, you MUST click on "Reload game box image" from the Control Panel in order your update can be taken into account. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript method "dontPreloadImage" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] 40266cef396b2576bf69a369b35f066d285ba3c9 Game meta-information: gameinfos.inc.php 0 130 952 2013-09-30T16:45:33Z Sourisdudesert 1 Created page with " From this file, you can edit the various meta-information of your game. Once you modified the file, don't forget to click on "Reload game informations" from the Control Pane..." wikitext text/x-wiki From this file, you can edit the various meta-information of your game. Once you modified the file, don't forget to click on "Reload game informations" from the Control Panel in order in can be taken into account. Most information provided in this file are self-explainable. Some information, though: * You are not allowed to set the "is_beta" to 0 before the game has been released on BGA and stabilized. * "fast/medium/slow_additional_time": please set high values here: after the game has been released, we will lower these value to match the real game duration. * "players": during the first step of development of a game, you'd better allow the "1 player" configuration: much easy to start/stop a game this way. * suggest_player_number / not_recommend_player_number: don't specify anything here if there is no configuration that is REALLY better/worst than another one. You can check player's poll on BoardGameGeek game page if you have any doubt. a7f2e3b93ea9584d908256fad7ce012a25fbc445 953 952 2013-09-30T16:53:46Z Sourisdudesert 1 wikitext text/x-wiki From this file, you can edit the various meta-information of your game. Once you modified the file, don't forget to click on "Reload game informations" from the Control Panel in order in can be taken into account. Most information provided in this file are self-explainable. Some information, though: * You are not allowed to set the "'''is_beta'''" to 0 before the game has been released on BGA and stabilized. * "'''fast/medium/slow_additional_time'''": please set high values here: after the game has been released, we will lower these value to match the real game duration. * "'''players'''": during the first step of development of a game, you'd better allow the "1 player" configuration: much easy to start/stop a game this way. * '''suggest_player_number''' / '''not_recommend_player_number''': don't specify anything here if there is no configuration that is REALLY better/worst than another one. You can check player's poll on BoardGameGeek game page if you have any doubt. == Tags == Any number of '''Tags''' can be attribute to your game. Tags are useful to place your game in the correct place and to give a good quick overview of what the game is about to players. Main game category (you MUST specify one tag AND ONLY ONE from this category): * 1: Abstract game * 2: Casual games * 3: For regular players * 4: For core gamers Other tags (you can specify any numbers of tags): * 10: Short game (<10 minutes) * 11: Medium length game (10 minutes to 30 minutes) * 12: Long game (>30mn) * 20: Awarded game (Win a prestigious award) * 22: Prototype (This game has not been published yet) * 23: Classic (This game is a classic from Public Domain) * 30: 2 players (2p game / best with 2 players) * 100: Fantasy theme * 101: Science Fiction them * 102: Historical theme * 103: Adventure * 104: Exploration * 105: Conquest * 106: Building * 200: Card game (Cards plays a central role in this game) * 201: Dice * 202: Solo game * 203: Worker placement * 204: Hand management * 205: Bluff * 206: Tile placement * 207: Combinations * 208: Majority * 209: Race * 210: Collection b7adfde2045949c9bb7e966a50dc3a89a4321342 976 953 2013-10-07T16:25:29Z Sourisdudesert 1 wikitext text/x-wiki From this file, you can edit the various meta-information of your game. Once you modified the file, don't forget to click on "Reload game informations" from the Control Panel in order in can be taken into account. Most information provided in this file are self-explainable. Some information, though: * You are not allowed to set the "'''is_beta'''" to 0 before the game has been released on BGA and stabilized. * "'''fast/medium/slow_additional_time'''": please set high values here: after the game has been released, we will lower these value to match the real game duration. * "'''players'''": during the first step of development of a game, you'd better allow the "1 player" configuration: much easy to start/stop a game this way. * '''suggest_player_number''' / '''not_recommend_player_number''': don't specify anything here if there is no configuration that is REALLY better/worst than another one. You can check player's poll on BoardGameGeek game page if you have any doubt. == Tags == Any number of '''Tags''' can be attribute to your game. Tags are useful to place your game in the correct place and to give a good quick overview of what the game is about to players. Main game category (you MUST specify one tag AND ONLY ONE from this category): * 1: Abstract game * 2: Casual games * 3: For regular players * 4: For core gamers Other tags (you can specify any numbers of tags): * 10: Short game (<10 minutes) * 11: Medium length game (10 minutes to 30 minutes) * 12: Long game (>30mn) * 20: Awarded game (Win a prestigious award) (the game must have been at the '''first''' place of one the [http://boardgamegeek.com/wiki/page/Gaming_Industry_Awards# following major awards list]). * 22: Prototype (This game has not been published yet) * 23: Classic (This game is a classic from Public Domain) * 30: 2 players (2p game / best with 2 players) * 100: Fantasy theme * 101: Science Fiction them * 102: Historical theme * 103: Adventure * 104: Exploration * 105: Conquest * 106: Building * 200: Card game (Cards plays a central role in this game) * 201: Dice * 202: Solo game * 203: Worker placement * 204: Hand management * 205: Bluff * 206: Tile placement * 207: Combinations * 208: Majority * 209: Race * 210: Collection 9a707a842ac57ac98cd686c8adb1c31d424af888 Stock 0 97 954 731 2013-09-30T16:54:40Z Sourisdudesert 1 /* Using stock: a simple example */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_themeurl+'img/your_game_name/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly 01488763215a10369fb58c28d5d29f666d3df21a 955 954 2013-09-30T16:55:31Z Sourisdudesert 1 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly 84276d4edcefbc9680bc48c07d1d64a51b57518e Studio 0 49 956 951 2013-09-30T17:00:44Z Sourisdudesert 1 /* BGA Studio user guide */ wikitext text/x-wiki [[File:Bga_studio_small.jpg]] Note: Please DO NOT translate Studio Documentation, so that there can be one place where you can find the latest information available. == What is Board Game Arena Studio? == '''Board Game Arena Studio''' is a platform to build online board game adaptation using the Board Game Arena platform. It is open to any gamer with development skills :) BGA Studio website: http://en.studio.boardgamearena.com (original announcement on BGA forum: http://forum.boardgamearena.com/viewtopic.php?f=10&t=1973) == Discover BGA Studio in 5 presentations == Why, how, what... to start discovering BGA Studio, we prepared 5 "powerpoint" presentations for you: * [http://www.slideshare.net/boardgamearena/5-reasons-why-you-should-use-bga-studio-for-your-online-board-game 5 reasons why you should use BGA Studio for your online board game] * [http://www.slideshare.net/boardgamearena/the-8-steps-to-create-a-board-game-on-board-game-arena The 8 steps to create a board game on Board Game Arena] * [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] * [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] * [http://www.slideshare.net/boardgamearena/bga-studio-guidelines BGA developers guidelines] == How to join the BGA developer team? == Please see this page: [[How to join BGA developer team?]] == Great, I'm in! ... How should I start? == If you didn't already, check the presentations at the top of this page to get the basics. Then, you should checkout the [[First steps with BGA Studio]] to make sure that runs fine. After that, we advise you to take a peek at one or both of these two game creation tutorials: * [[Tutorial reversi]] * [[Tutorial gomoku]] Then start editing files and see what happens! ;) If you have any questions, please check out the '''[[Studio FAQ]]''' first, then if you didn't find the answer you were looking for, please post your question on the [http://forum.boardgamearena.com/viewforum.php?f=12 '''development forum''']. == BGA Studio documentation == === BGA Studio Framework reference === This part of the documentation focuses on the development framework itself: functions and methods available to build your game. [[Studio file reference|File structure of a BGA game]] ==== Game logic ==== * [[Main game logic: yourgamename.game.php]] * [[Your game state machine: states.inc.php]] * [[Game database model: dbmodel.sql]] * [[Players actions: yourgamename.action.php]] * [[Game material description: material.inc.php]] * [[Game statistics: stats.inc.php]] ==== Game interface ==== * [[Game interface logic: yourgamename.js]] * [[Game art: img directory]] * [[Game interface stylesheet: yourgamename.css]] * [[Game layout: view and template: yourgamename.view.php and yourgamename_yourgamename.tpl]] ==== Other components ==== * [[Translations]] (how to make your game translatable) * [[Game options and preferences: gameoptions.inc.php]] * [[Game meta-information: gameinfos.inc.php]] * [[Game replay]] === BGA Studio game components reference === Game components are useful tools you can use in your game adaptations. * [[Deck]]: a PHP component to manage cards (deck, hands, picking cards, moving cards, shuffle deck, ...). * [[Counter]]: a JS component to manage a counter that can increase/decrease (ex: player's score). * [[Draggable]]: a JS component to manage drag'n'drop actions. * [[ExpandableSection]]: a JS component to manage a rectangular block of HTML than can be displayed/hidden. * [[Scrollmap]]: a JS component to manage a scrollable game area (useful when the game area can be infinite. Examples: Saboteur or Takenoko games). * [[Stock]]: a JS component to manage and display a set of game elements displayed at a position. * [[Wrapper]]: a JS component to wrap a &lt;div&gt; element around his child, even if these elements are absolute positioned. * [[Zone]]: a JS component to manage a zone of the board where several game elements can come and leave, but should be well displayed together (See for example: token's places at Can't Stop). === BGA Studio user guide === This part of the documentation is a user guide for the BGA Studio online development environment. * [[Tools and tips of BGA Studio]] * [[Practical debugging]] * [[Studio logs]] * [[Studio FAQ]] == BGA Developer team organization == * [[Steps to create a BGA game]] * [[Post-release phase]] == Other resources == [http://forum.boardgamearena.com/viewforum.php?f=12 Development forum] [http://forum.boardgamearena.com/viewforum.php?f=4 Bugs forum] ac77cc3aebc248727c5d63ba7c05bcfed8a8a923 Steps to create a BGA game 0 117 957 809 2013-09-30T17:04:00Z Sourisdudesert 1 wikitext text/x-wiki Here's a summary of the different steps you would follow when developing a game with BGA Studio. {| class="wikitable" |- ! Step !! How to reach this ste?p !! What happened during the step? |- | Initial || [[How to join BGA developer team?]] || You can choose to join an existing team / create a new project |- | Assigned || You choosed a game || You can start the development of the game |- | Pre-alpha || You've started to write some piece of code || You develop the game. During this phase, we can assist you with the framework and give you some pieces of advice. |- | Alpha || You tell us that your development is finished || "BGA review": we are reviewing your game and check if it respects [http://fr.slideshare.net/boardgamearena/bga-studio-guidelines BGA guidelines]. If not, we will ask you (and help you) to fix them. |- | Private beta || We give a "go" || "Publisher review": On preproduction platform, the publisher, the designer, we and you can test the game together and separately. We help you to take into account remarks from the publisher and the designer. |- | Public beta || The adaptation is approved by the publiher || We find together a good launch date for the game, we announce the game on BGA news, and then player can start to play! During the first days, it is common that some bugs are reported by players, and you can fix them following the instructions in [[Post-release phase]]. |- | Gold || The game is stable on BGA || Congrats! You can still modify and optimize things following the instructions in [[Post-release phase]]. |} 3057282ef1dc5e90cca44ca7d9c29442ffdfe22d Post-release phase 0 118 958 825 2013-09-30T17:10:52Z Sourisdudesert 1 wikitext text/x-wiki Your game is now on BGA: congrats! But what happened when there are some bugs to fix or when you want to optimize something? Don't be afraid: you're still allowed to modify your game. You just have to pay attention to the points below. == Bugs reporting == Bugs are reported in the [http://forum.boardgamearena.com/viewforum.php?f=4 BGA bugs forum]. During days after your game has been published and from time to time, please have a look at it to check if everything is fine. == How to submit changes? == There is 3 (short) steps to make your changes visible on BGA (from your Control Panel): * Commit your changes. * Build a new version (don't forget to do a successful commit BEFORE your build). * Deploy your new version in production. == What can be modified after release? == Everything can be modified. BUT, some items requires a special attention, and you must inform us in some cases: ===Changes that breaks the games in progress=== Some changes will break the games in progress at the moment the release/the hotfix will be performed. Each time you make a change, you should ask you the question "it is safe to make this change in a game in progress", and if the answer is "no" you have to inform us. Example of changes that break the games in progress: * Changes in the database schema of the game (dbmodel.sql). * New global variable or game option accessed during the game (if it's only used during setup, it should be safe). * New statistic (it won't be initialized properly, so it's going to crash the game). * Change ID of existing game states (adding new game states is fine). Of course, as a rule of thumb, you should avoid to introduce changes that break a game in progress. Sometimes however, you do not have any other choice. In this case: * Try to group all your updates in one version, thus we won't have to block your game several times. * Tell us explicitly that you introduce some update that can break games in progress so we can block the game during a short time. Note: in the near future, we will introduce the possibility for you to block/unblock a game directly from your Control Panel in order to perform all the process by yourself. === Updating Statistics === You should be careful when updating a statistics: * If you want to add a new statistics, please refer to the paragraph above ("changes that breaks the games in progress"). * If you want to update a statistic, please update it and do not remove/create another one. Otherwise, the statistic won't keep the same ID and players will lost all the historical statistics data. * If your game is published on BGA, please don't remove any statistics (historical data will be lost). === Updating string to be translated === When you update a string that has been marked to be translatable, please keep in mind that all current translations done by the BGA community will be lost. Consequently, when you are about to modify a string to be translated (after release), please ask you the following questions: * Is it just an English misspelling? In this case, it is better to fix the English translation of the string than the original string to be translated. * Has the meaning of the string changed? If yes, you HAVE to change the original string in order to invalidate all translations that has been done already. * Is there a similar string already used elsewhere in my game? In this case, you'd better use it again to enjoy immediately all translations already available. ===Major changes=== If you do some major changes to your game like: * Introducing a new expansion. * Major code rewriting/refactoring. ... please tell us. In this case, we can: * Make your game back from "gold" to "public beta", to incite player to report bugs. * And eventually, publish a news about it :) 860172b542fdcbafd19c16c6a60b8007a7c77266 BGA Game licenses 0 131 959 2013-09-30T19:38:22Z Sourisdudesert 1 Created page with " == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this boar..." wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the list below? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. Important: you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == You should take into account the fol == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: 0: In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. 1: At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 2: Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. 3: Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! 4: We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 695cb0256a099b6554c86258e0822172848a611f 960 959 2013-09-30T19:38:48Z Sourisdudesert 1 /* What if I start developing a game that is not on the list below? */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. Important: you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == You should take into account the fol == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: 0: In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. 1: At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 2: Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. 3: Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! 4: We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 93bf4d69ac7675fd10a26554fcdbc8beae355543 961 960 2013-09-30T19:39:27Z Sourisdudesert 1 /* Wow, I WANT to develop a game on this list! */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. Important: you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == You should take into account the fol == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: 0: In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. 1: At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 2: Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. 3: Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! 4: We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 7abedae86b4ee8ee613db9c3c10357521b22c3ae 962 961 2013-09-30T19:39:56Z Sourisdudesert 1 /* What has to be done to add a game on this list? */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == You should take into account the fol == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: 0: In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. 1: At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 2: Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. 3: Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! 4: We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. c783045c457de98285b6e6366084d147ad7cf7ee 963 962 2013-09-30T19:53:15Z Sourisdudesert 1 /* I want my prototype/unpublished game on BGA */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes don't have a big audience on BGA. * we also discourage you to develop your own game: we build better games adaptation with a designer that focus on the gameplay and a developer that focus on the realization. So basically you can develop your prototype/unpublished game on BGA, but we encourage you to do it only if you are in one/several of the situation below: * the game is going to be published in the near future. * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...). * you want to test a game with a big number of players to finalize some minor things ("balance"). == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: 0: In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. 1: At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 2: Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. 3: Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! 4: We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 19303f4c227bf19a3b897b61ee7148f3592a5d7b 964 963 2013-09-30T19:54:07Z Sourisdudesert 1 /* What if I really want to develop a particular game that is NOT on the list below? */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes don't have a big audience on BGA. * we also discourage you to develop your own game: we build better games adaptation with a designer that focus on the gameplay and a developer that focus on the realization. So basically you can develop your prototype/unpublished game on BGA, but we encourage you to do it only if you are in one/several of the situation below: * the game is going to be published in the near future. * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...). * you want to test a game with a big number of players to finalize some minor things ("balance"). == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: === 0: === In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. === 1: === At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: _ check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). _ check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. _ very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 === 2: === Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! === 4: === We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. c63fc6b89c725461f6a5a66b986dd8c92d176159 965 964 2013-09-30T19:55:45Z Sourisdudesert 1 /* 1: */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes don't have a big audience on BGA. * we also discourage you to develop your own game: we build better games adaptation with a designer that focus on the gameplay and a developer that focus on the realization. So basically you can develop your prototype/unpublished game on BGA, but we encourage you to do it only if you are in one/several of the situation below: * the game is going to be published in the near future. * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...). * you want to test a game with a big number of players to finalize some minor things ("balance"). == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: === 0: === In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. === 1: === At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 === 2: === Read the publisher list below to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! === 4: === We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. b6c44a94b871bfa92a9307663bda5a41f4d0ed7b 966 965 2013-09-30T19:56:30Z Sourisdudesert 1 /* 2: */ wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes don't have a big audience on BGA. * we also discourage you to develop your own game: we build better games adaptation with a designer that focus on the gameplay and a developer that focus on the realization. So basically you can develop your prototype/unpublished game on BGA, but we encourage you to do it only if you are in one/several of the situation below: * the game is going to be published in the near future. * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...). * you want to test a game with a big number of players to finalize some minor things ("balance"). == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: === 0: === In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. === 1: === At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 === 2: === Read the publisher list from the "available licence" page to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! === 4: === We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 26c04bdf3793ee0fa5b163639693fe822930ff25 967 966 2013-09-30T19:57:56Z Sourisdudesert 1 wikitext text/x-wiki == What is a "licence" == Almost all games on Board Game Arena platform are adaptations of existing commercial "real" board games. To propose an adaption of one of this board game on a website, the right owners must grant a licence/authorization to this website. == Are all games on BGA licenced by their respective right owners? == Yes. == Where can I find available games licences == http://en.studio.boardgamearena.com/#!page/availablelicences == What if I start developing a game that is not on the "Available licences"? == We allow developers to start any project on the Studio. But when your project is going to be live on BGA, it will be rejected during the review process. Exception: games on the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Cool :) But before creating a new project please takes some seconds to check if someone is not already developing this game. If this is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What has to be done to add a game on this list? == The right owners has to give an licence/authorization to BGA. Publishers do not have to pay anything to BGA for this, they just have to give their "go". Most of the time, publishers wants to have a formal agreement and we signed a contract. '''Important''': you CAN'T take the initiative to ask a licence authorization on behalf of BGA, because the authorization has to be granted to BGA (read more below). == Why all best-sellers games I love are not on that list? == As you can imagine, the more popular a game is, the most difficult it is to have an agreement to host this game on BGA. So if you think about a particular very popular game that is not on this list, the reason for this rather be "we asked and this was impossible" than "we did'nt think of this". We are pretty lucky to have a very nice platform that convince the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want my prototype/unpublished game on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes don't have a big audience on BGA. * we also discourage you to develop your own game: we build better games adaptation with a designer that focus on the gameplay and a developer that focus on the realization. So basically you can develop your prototype/unpublished game on BGA, but we encourage you to do it only if you are in one/several of the situation below: * the game is going to be published in the near future. * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...). * you want to test a game with a big number of players to finalize some minor things ("balance"). == What if I really want to develop a particular game that is NOT on the list below? == This is the most asked question, to please follow the instructions: === 0: === In any case: please, do not contact publishers without asking us before. Please imagine you are a game publisher who just published a popular game: what would you do if you received 40 emails from 40 developers speaking about "BGA platform" and wanting to build an adaptation on it? Continue to 1. === 1: === At first, you should determine who is the owner of the electronic rights of a game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because in general board games are translated&published in each countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost all the time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the publisher from your country and the other one is the original publisher. Particular case: if the game is themed with a prestigious licence (Star Wars, Lord of the Ring, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licences owners are very restrictive, and there is 99% chance that an online adaptation on BGA is not welcome. Continue to 2 === 2: === Read the publisher list from the "available licence" page to check if BGA already work with this publisher. If YES, continue to 3. If NO, continue to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens sometimes that some publishers are not convinced by online boardgaming and this is their right to think this. BGA team regularly meet new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already a game licenced by this publisher that has to be developed? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a licence for a game, a publisher expect that the game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop a game from this particular publisher, the best option for you is to help to develop the game that is currently licenced and then to ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we would be happy to ask the publisher the authorization! === 4: === We never worked with this publisher before. If you have already developed a game on BGA, we would be happy to ask the publisher the authorization :) If this is your first game on BGA, we probably advise you to start with a game already on the list. Why? Because unfortunately, some developers are asking us to request a licence, and don't finish the development afterwards. When we ask for a licence, we are more comfortable if you already show that you are able to realize an adaptation. Particular case: if you already know the publisher PERSONALLY, or have some contact there, please tell us so we can help you to setup a contract to host a game adaptation on BGA. 04a8e9ded7e7fa61ef6e9a9a76858da7938e4856 970 967 2013-10-01T20:52:40Z Een 3 wikitext text/x-wiki == What's a "license" == Almost all games on the Board Game Arena platform are adaptations of existing commercial "real" board games. In order to host an adaptation of one of these board games on a website, the copyright owners must first grant a license/authorisation to this website. == Are all games on BGA licensed by their respective copyright owners? == Yes, for all games that are not in the public domain, BGA has been granted an authorisation to create and host an online version. == Where can I find a list of games for which a license has already been granted? == [http://en.studio.boardgamearena.com/#!page/availablelicences Check the list of available licenses] (NB: this page is restricted to developers registered in BGA studio. You'll need to enter your BGA studio credentials to access it) == What if I start developing a game that is not on the "Available licences" list? == We allow developers to start any project in the Studio. But when you submit your project to go live on BGA, it will be rejected during the review process if BGA doesn't hold a proper license for the game. Exception: games in the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Great! :) But before creating a new project, please takes some seconds to check that someone is not already developing this game. If it is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What needs to be done to add a game on this list? == The copyright owners have to give an license/authorisation to BGA. Publishers do not have to pay anything to BGA for this, they just need to give their "go". Most of the time, publishers want to have a formal agreement and we sign a contract with them. '''Important''': you CAN'T take the initiative to ask a license authorization on behalf of BGA, because the authorisation has to be granted to BGA (read more below). == Why aren't all the best-selling games that I love on that list? == As you can imagine, the more popular a game is, the most difficult it can be to get an agreement to host this game on BGA. So if you are thinking about a very popular game that is not on this list, the reason is most likely "we asked but it was not possible" and not "we didn't think of it". We are really happy to have been able to build a very nice platform that managed to convince some of the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want to make my prototype/unpublished game available on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes most likely won't get a big audience on BGA. * we also advise you against developing your own game: games adaptation are better when there is a designer whose focus is on the gameplay and a developer whose focus is on the implementation. So you definitely can develop your prototype/unpublished game on BGA, but we encourage you to think twice about it and do it only if you are in one/several of the situations below: * the game is going to be published in the near future * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...) * you want to test your game with a big number of players to fine-tune some minor things ("balance" of the game). == What if I really want to develop a particular game that is NOT on the list below? == This is the most frequently asked question, so for a detailed answer please follow the white rabbit down these numbered instructions: === 0: === In any case: please, do not contact publishers without asking us before. Just put yourself in the shoes of a game publisher who just published a popular game: what would you think if you received 40 emails from 40 developers speaking about some "BGA platform" and wanting to build an adaptation on it? Proceed to 1. === 1: === First, you should determine who is the owner of the digital rights of the game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because very often board games are translated and published in different countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably Polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost every time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the local publisher from your country and the other one is the original publisher. Special case: if the game is themed with a prestigious licence (Star Wars, Lord of the Rings, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licenses owners are very restrictive, and there is 99% chance that an online adaptation on BGA would be impossible to manage. Continue to 2. === 2: === Read the list of publishers from the "available licenses" page to check if BGA is already working with this publisher. If YES, proceed to 3. If NO, jump to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens that some publishers are not convinced or interested by online boardgaming and it is their right to think so. The BGA team regularly meets new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already in the list a game licensed by this publisher that is available to be developed or under development? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a license for a game, the publisher naturally expects that this game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop another game from this particular publisher, the best option for you is to help develop the game that is currently licensed and then ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we will be happy to ask the publisher for his authorisation. === 4: === We have never worked with this publisher before. If you have already developed a game on BGA, we will be happy to contact the publisher to ask for an authorisation :) If this is your first game on BGA, we advise you to start with a game already on the list. Why? Because unfortunately, some developers ask us to request a license, and then don't finish the development. When we ask for a license, we are more comfortable if you have already shown your ability to realise an adaptation. Special case: if you know the publisher PERSONALLY, or have some contact there, please tell us so we can help you setup a contract allowing us to host a game adaptation on BGA. 7e176a08204233bebc4a4d93360a66c2b440a050 971 970 2013-10-01T20:54:58Z Een 3 wikitext text/x-wiki == What is a "license" exactly? == Almost all games on the Board Game Arena platform are adaptations of existing commercial "real" board games. In order to host an adaptation of one of these board games on a website, the copyright owners must first grant a license/authorisation to this website. == Are all games on BGA licensed by their respective copyright owners? == Yes, for all games that are not in the public domain, BGA has been granted an authorisation to create and host an online version. == Where can I find a list of games for which a license has already been granted? == [http://en.studio.boardgamearena.com/#!page/availablelicences Check the list of available licenses] (NB: this page is restricted to developers registered in BGA studio. You'll need to enter your BGA studio credentials to access it) == What if I start developing a game that is not on the "Available licences" list? == We allow developers to start any project in the Studio. But when you submit your project to go live on BGA, it will be rejected during the review process if BGA doesn't hold a proper license for the game. Exception: games in the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Great! :) But before creating a new project, please takes some seconds to check that someone is not already developing this game. If it is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What needs to be done to add a game on this list? == The copyright owners have to give an license/authorisation to BGA. Publishers do not have to pay anything to BGA for this, they just need to give their "go". Most of the time, publishers want to have a formal agreement and we sign a contract with them. '''Important''': you CAN'T take the initiative to ask a license authorization on behalf of BGA, because the authorisation has to be granted to BGA (read more below). == Why aren't all the best-selling games that I love on that list? == As you can imagine, the more popular a game is, the most difficult it can be to get an agreement to host this game on BGA. So if you are thinking about a very popular game that is not on this list, the reason is most likely "we asked but it was not possible" and not "we didn't think of it". We are really happy to have been able to build a very nice platform that managed to convince some of the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want to make my prototype/unpublished game available on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes most likely won't get a big audience on BGA. * we also advise you against developing your own game: games adaptation are better when there is a designer whose focus is on the gameplay and a developer whose focus is on the implementation. So you definitely can develop your prototype/unpublished game on BGA, but we encourage you to think twice about it and do it only if you are in one/several of the situations below: * the game is going to be published in the near future * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...) * you want to test your game with a big number of players to fine-tune some minor things ("balance" of the game). == What if I really want to develop a particular game that is NOT on the list below? == This is the most frequently asked question, so for a detailed answer please follow the white rabbit down these numbered instructions: === 0: === In any case: please, do not contact publishers without asking us before. Just put yourself in the shoes of a game publisher who just published a popular game: what would you think if you received 40 emails from 40 developers speaking about some "BGA platform" and wanting to build an adaptation on it? Proceed to 1. === 1: === First, you should determine who is the owner of the digital rights of the game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because very often board games are translated and published in different countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably Polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost every time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the local publisher from your country and the other one is the original publisher. Special case: if the game is themed with a prestigious licence (Star Wars, Lord of the Rings, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licenses owners are very restrictive, and there is 99% chance that an online adaptation on BGA would be impossible to manage. Continue to 2. === 2: === Read the list of publishers from the "available licenses" page to check if BGA is already working with this publisher. If YES, proceed to 3. If NO, jump to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens that some publishers are not convinced or interested by online boardgaming and it is their right to think so. The BGA team regularly meets new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already in the list a game licensed by this publisher that is available to be developed or under development? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a license for a game, the publisher naturally expects that this game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop another game from this particular publisher, the best option for you is to help develop the game that is currently licensed and then ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we will be happy to ask the publisher for his authorisation. === 4: === We have never worked with this publisher before. If you have already developed a game on BGA, we will be happy to contact the publisher to ask for an authorisation :) If this is your first game on BGA, we advise you to start with a game already on the list. Why? Because unfortunately, some developers ask us to request a license, and then don't finish the development. When we ask for a license, we are more comfortable if you have already shown your ability to realise an adaptation. Special case: if you know the publisher PERSONALLY, or have some contact there, please tell us so we can help you setup a contract allowing us to host a game adaptation on BGA. f7fa9a78f5dd8ccffc5028a8d1ee6f5d98648d5b 972 971 2013-10-01T20:56:18Z Een 3 moved [[BGA Game licences]] to [[BGA Game licenses]]: typo wikitext text/x-wiki == What is a "license" exactly? == Almost all games on the Board Game Arena platform are adaptations of existing commercial "real" board games. In order to host an adaptation of one of these board games on a website, the copyright owners must first grant a license/authorisation to this website. == Are all games on BGA licensed by their respective copyright owners? == Yes, for all games that are not in the public domain, BGA has been granted an authorisation to create and host an online version. == Where can I find a list of games for which a license has already been granted? == [http://en.studio.boardgamearena.com/#!page/availablelicences Check the list of available licenses] (NB: this page is restricted to developers registered in BGA studio. You'll need to enter your BGA studio credentials to access it) == What if I start developing a game that is not on the "Available licences" list? == We allow developers to start any project in the Studio. But when you submit your project to go live on BGA, it will be rejected during the review process if BGA doesn't hold a proper license for the game. Exception: games in the public domain are of course not concerned. == Wow, I WANT to develop a game on this list! == Great! :) But before creating a new project, please takes some seconds to check that someone is not already developing this game. If it is the case, maybe you can propose to join the project? [http://en.studio.boardgamearena.com/#!projects Check the list of current projects] == What needs to be done to add a game on this list? == The copyright owners have to give an license/authorisation to BGA. Publishers do not have to pay anything to BGA for this, they just need to give their "go". Most of the time, publishers want to have a formal agreement and we sign a contract with them. '''Important''': you CAN'T take the initiative to ask a license authorization on behalf of BGA, because the authorisation has to be granted to BGA (read more below). == Why aren't all the best-selling games that I love on that list? == As you can imagine, the more popular a game is, the most difficult it can be to get an agreement to host this game on BGA. So if you are thinking about a very popular game that is not on this list, the reason is most likely "we asked but it was not possible" and not "we didn't think of it". We are really happy to have been able to build a very nice platform that managed to convince some of the most prestigious publishers to host their games here, but of course we can't convince -everyone-. == I want to make my prototype/unpublished game available on BGA == This is possible, BUT most of the time, we consider this is a bad idea for the following reasons: * if your game has not been published yet, there is certainly a good reason - most of the time there is still some more work to do on the game itself. Publishing a game on BGA that is not 100% finalized won't help you to finalize it. * as a rule of thumb, popular board games on BGA are games that are popular in the real world. As a consequence prototypes most likely won't get a big audience on BGA. * we also advise you against developing your own game: games adaptation are better when there is a designer whose focus is on the gameplay and a developer whose focus is on the implementation. So you definitely can develop your prototype/unpublished game on BGA, but we encourage you to think twice about it and do it only if you are in one/several of the situations below: * the game is going to be published in the near future * the game design process is really 100% done: the game is ready to be published in its current shape (ex: self-publishing game, game in a crowdfunding process...) * you want to test your game with a big number of players to fine-tune some minor things ("balance" of the game). == What if I really want to develop a particular game that is NOT on the list below? == This is the most frequently asked question, so for a detailed answer please follow the white rabbit down these numbered instructions: === 0: === In any case: please, do not contact publishers without asking us before. Just put yourself in the shoes of a game publisher who just published a popular game: what would you think if you received 40 emails from 40 developers speaking about some "BGA platform" and wanting to build an adaptation on it? Proceed to 1. === 1: === First, you should determine who is the owner of the digital rights of the game. Most of the time, this is the original publisher of the game. The original publisher is not always easy to find, because very often board games are translated and published in different countries by a local publisher. To find the original publisher: * check in which country the game has been published first (ex: If it's in Poland, the original publisher is probably Polish). * check the copyright notice at the end of the rules, where the original publisher is mentioned almost every time. * very often, if two publishers are mentioned on a gamebox, the well-known publisher is the local publisher from your country and the other one is the original publisher. Special case: if the game is themed with a prestigious licence (Star Wars, Lord of the Rings, some TV show, some well known novel...), you can stop here immediately: contracts between game publishers and prestigious licenses owners are very restrictive, and there is 99% chance that an online adaptation on BGA would be impossible to manage. Continue to 2. === 2: === Read the list of publishers from the "available licenses" page to check if BGA is already working with this publisher. If YES, proceed to 3. If NO, jump to 4. However, if the publisher is listed as "not working with BGA", you can stop here immediately. It happens that some publishers are not convinced or interested by online boardgaming and it is their right to think so. The BGA team regularly meets new publishers during board games events to show them the platform, so we're working on this but -again- we cannot convince everyone. === 3: === Is there already in the list a game licensed by this publisher that is available to be developed or under development? If YES: as a rule of thumb, we do not ask more than one game to a publisher at a time. When we ask a license for a game, the publisher naturally expects that this game will be developed and published online. So we develop and publish a game before asking for another. If you really want to develop another game from this particular publisher, the best option for you is to help develop the game that is currently licensed and then ask us to request the game you'd like to develop. If NO: just ask us which game you want to develop and we will be happy to ask the publisher for his authorisation. === 4: === We have never worked with this publisher before. If you have already developed a game on BGA, we will be happy to contact the publisher to ask for an authorisation :) If this is your first game on BGA, we advise you to start with a game already on the list. Why? Because unfortunately, some developers ask us to request a license, and then don't finish the development. When we ask for a license, we are more comfortable if you have already shown your ability to realise an adaptation. Special case: if you know the publisher PERSONALLY, or have some contact there, please tell us so we can help you setup a contract allowing us to host a game adaptation on BGA. f7fa9a78f5dd8ccffc5028a8d1ee6f5d98648d5b Faq 0 3 968 911 2013-10-01T19:06:03Z Een 3 /* What do I need to play? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[Browser_support|browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Thinking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 0cda94e7386cdf0bfd3e819363c68c75b4f78c9b Browser support 0 13 969 426 2013-10-01T19:11:36Z Een 3 wikitext text/x-wiki <h3>For the best experience, we recommend:</h3> * '''Google Chrome 10+''' [http://www.google.com/chrome Windows] [http://www.google.com/chrome?platform=mac Mac] [http://www.google.com/chrome?platform=linux Linux] * '''Mozilla Firefox 4+''' [http://www.mozilla.org/products/firefox/ Windows] [http://www.mozilla.org/products/firefox/ Mac] [http://www.mozilla.org/products/firefox/ Linux] Board Game Arena takes advantage of the most recent web technology to make it possible for you to play without installing anything on your computer: no software to download or update, no plugins to install, etc. Consequently, to play you need to use a modern web browser. Generally speaking, the more recent your web browser is, the more pleasant your game experience on Board Game Arena will be. This website makes intensive use of Javascript and your browser's graphics capabilities. Thus, if you want to have the best gaming experience with Board Game Arena, you should use one of the browsers listed above. However, we officially support the following browsers: * Google Chrome 4+ * Mozilla Firefox 3.5+ * Internet Explorer 9+ * Safari 4+ (Note: We do NOT support playing on iPads or similar tablets.) [[Category:Help]] 8a4654e948afc3ad04edef323b0769cd8cdcae40 BGA Game licences 0 132 973 2013-10-01T20:56:18Z Een 3 moved [[BGA Game licences]] to [[BGA Game licenses]]: typo wikitext text/x-wiki #REDIRECT [[BGA Game licenses]] a99c9debf146a8f72d9071adcdf88894fcd0b39a Gamehelpmachiavelli 0 133 974 2013-10-03T20:53:55Z Emanueles 3943 Created page with " == Game materials == Machiavelli is played with 2 or more french decks without using jokers. == Goal of the game == The game ends when a player plays all his cards or when t..." wikitext text/x-wiki == Game materials == Machiavelli is played with 2 or more french decks without using jokers. == Goal of the game == The game ends when a player plays all his cards or when the deck is depleted. == Game setup == Every player get 15 cards. Cards are secret and visible by its owner only. Put the rest of the deck on the table. == How to play == Each turn players can do a sequence of action, until the turn ends. Actions: * ''Draw a card'' The player can draw a card from the deck and his turn ends. * ''Play cards'' Player can play cards from his hand, laying them on the table and respecting the following rules: 1) Cards on the table must always form groups that contain at least 3 cards. 2) Groups on the table must be sets or runs. - Sets are groups of 3 or 4 cards of the same value (e.g. Q♥, Q♦, Q♣). - Runs are groups of cards of the same suit whose values are in sequence (e.g. 8♥,9♥,10♥,J♥,Q♥). The card rank start from ace to king: A, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K. Cards can be played also melding them to an existing group on the table as long as the previous rules are respected (e.g. player decides to meld 3♣, 7♣, 8♣ to an existing run on the table, made by 4♣, 5♣, 6♣ of clubs. He can meld the cards in the right sequence). * ''Take cards'' Player can take one or more groups of cards from the table adding them to his hand. Before his turn ends, he must play all cards he took from the table. Player may play these cards creating new groups, different from the previous ones, and using cards he already had in hand too. Example: Groups on the table: 3♣, 4♣, 5♣, 6♣ Q♥, Q♦, Q spades Player's hand: 5♣, 7♣, 8♣, Q♣, J♥, K♥ Player takes the 2 groups on the table, then he plays cards creating these new groups: 3♣, 4♣, 5♣ 5♣, 6♣, 7♣, 8♣ J♥, Q♥, K♥ Q♦, Q♠, Q♣ * ''Restore'' If a player took cards from the table and he is no more able to play all token cards, he may force his turn ends by restoring the original game situation on the table and of his hand. He must put back the token cards on the table, restoring the original groups and collecting cards he had in his hand that he may have played. Once restored, player must draw 3 cards from the deck as penalty. * ''Pass'' Player can pass only if he played cards and if he has no cards taken from the table in his hand. == End of the game == Game ends as soon as a player has no more cards in his hand or the deck is depleted. In the second case, the winner is the player with less cards in his hand. 88b66124042dac597c46d922ef5de478b913914c 975 974 2013-10-03T21:05:48Z Emanueles 3943 wikitext text/x-wiki == Game materials == Machiavelli is played with 2 or more french decks without using jokers. == Goal of the game == The game ends when a player plays all his cards or when the deck is depleted. == Game setup == Every player get 15 cards. Cards are secret and visible by its owner only. Put the rest of the deck on the table. == How to play == Each turn players can do a sequence of action, until the turn ends. Actions: * ''Draw a card'' The player can draw a card from the deck and his turn ends. * ''Play cards'' Player can play cards from his hand, laying them on the table and respecting the following rules: 1) Cards on the table must always form groups that contain at least 3 cards. 2) Groups on the table must be sets or runs. - Sets are groups of 3 or 4 cards of the same value (e.g. Q♥, Q♦, Q♣). - Runs are groups of cards of the same suit whose values are in sequence (e.g. 8♥,9♥,10♥,J♥,Q♥). The card rank start from ace to king: A, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K. Cards can be played also melding them to an existing group on the table as long as the previous rules are respected (e.g. player decides to meld 3♣, 7♣, 8♣ to an existing run on the table, made by 4♣, 5♣, 6♣ of clubs. He can meld the cards in the right sequence). * ''Take cards'' Player can take one or more groups of cards from the table adding them to his hand. Before his turn ends, he must play all cards he took from the table. Player may play these cards creating new groups, different from the previous ones, and using cards he already had in hand too. Example: Groups on the table: 3♣, 4♣, 5♣, 6♣ Q♥, Q♦, Q spades Player's hand: 5♣, 7♣, 8♣, Q♣, J♥, K♥ Player takes the 2 groups on the table, then he plays cards creating these new groups: 3♣, 4♣, 5♣ 5♣, 6♣, 7♣, 8♣ J♥, Q♥, K♥ Q♦, Q♠, Q♣ * ''Restore'' If a player took cards from the table and he is no more able to play all token cards, he may force his turn ends by restoring the original game situation on the table and of his hand. He must put back the token cards on the table, restoring the original groups and collecting cards he had in his hand that he may have played. Once restored, player must draw 3 cards from the deck as penalty. In the BGA version the ''Restore'' action can be automatically performed clicking on a specific button. Cards token from the table are tagged with a red sign as reminder to play it before passing. * ''Pass'' Player can pass only if he played cards and if he has no cards taken from the table in his hand. == End of the game == Game ends as soon as a player has no more cards in his hand or the deck is depleted. In the second case, the winner is the player with less cards in his hand. 084c9a0a090271d58fb69df9c8e4e1e09774add0 987 975 2013-10-21T12:01:19Z Nalafab 2897 /* How to play */ wikitext text/x-wiki == Game materials == Machiavelli is played with 2 or more french decks without using jokers. == Goal of the game == The game ends when a player plays all his cards or when the deck is depleted. == Game setup == Every player get 15 cards. Cards are secret and visible by its owner only. Put the rest of the deck on the table. == How to play == Each turn players can do a sequence of action, until the turn ends. Actions: * ''Draw a card'' The player can draw a card from the deck and his turn ends. * ''Play cards'' Player can play cards from his hand, laying them on the table and respecting the following rules: 1) Cards on the table must always form groups that contain at least 3 cards. 2) Groups on the table must be sets or runs. - Sets are groups of 3 or 4 cards of the same value (e.g. Q♥, Q♦, Q♣). - Runs are groups of cards of the same suit whose values are in sequence (e.g. 8♥,9♥,10♥,J♥,Q♥). The card rank start from ace to king: A, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K. Cards can be played also melding them to an existing group on the table as long as the previous rules are respected (e.g. player decides to meld 3♣, 7♣, 8♣ to an existing run on the table, made by 4♣, 5♣, 6♣ of clubs. He can meld the cards in the right sequence). * ''Take cards'' Player can take one or more groups of cards from the table adding them to his hand. Before his turn ends, he must play all cards he took from the table. Player may play these cards creating new groups, different from the previous ones, and using cards he already had in hand too. Example: Groups on the table: 3♣, 4♣, 5♣, 6♣ / Q♥, Q♦, Q♠ Player's hand: 5♣, 7♣, 8♣, Q♣, J♥, K♥ Player takes the 2 groups on the table, then he plays cards creating these new groups: 3♣, 4♣, 5♣ / 5♣, 6♣, 7♣, 8♣ / J♥, Q♥, K♥ / Q♦, Q♠, Q♣ * ''Restore'' If a player took cards from the table and he is no more able to play all token cards, he may force his turn ends by restoring the original game situation on the table and of his hand. He must put back the token cards on the table, restoring the original groups and collecting cards he had in his hand that he may have played. Once restored, player must draw 3 cards from the deck as penalty. In the BGA version the ''Restore'' action can be automatically performed clicking on a specific button. Cards token from the table are tagged with a red sign as reminder to play it before passing. * ''Pass'' Player can pass only if he played cards and if he has no cards taken from the table in his hand. == End of the game == Game ends as soon as a player has no more cards in his hand or the deck is depleted. In the second case, the winner is the player with less cards in his hand. d8faf700b45b7213c3d62812cebb4f362019a186 991 987 2013-11-12T21:51:15Z Emanueles 3943 /* How to play */ wikitext text/x-wiki == Game materials == Machiavelli is played with 2 or more french decks without using jokers. == Goal of the game == The game ends when a player plays all his cards or when the deck is depleted. == Game setup == Every player get 15 cards. Cards are secret and visible by its owner only. Put the rest of the deck on the table. == How to play == Each turn players can do a sequence of action, until the turn ends. Actions: * ''Draw a card'' The player can draw a card from the deck and his turn ends. * ''Play cards'' Player can play cards from his hand, laying them on the table and respecting the following rules: 1) Cards on the table must always form groups that contain at least 3 cards. 2) Groups on the table must be sets or runs. - Sets are groups of 3 or 4 cards of the same value (e.g. Q♥, Q♦, Q♣). - Runs are groups of cards of the same suit whose values are in sequence (e.g. 8♥,9♥,10♥,J♥,Q♥). The card rank start from ace to king: A, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, J, Q, K. Cards can be played also melding them to an existing group on the table as long as the previous rules are respected (e.g. player decides to meld 3♣, 7♣, 8♣ to an existing run on the table, made by 4♣, 5♣, 6♣ of clubs. He can meld the cards in the right sequence). * ''Take cards'' Player can take one or more groups of cards from the table adding them to his hand. Before his turn ends, he must play all cards he took from the table. Player may play these cards creating new groups, different from the previous ones, and using cards he already had in hand too. Example: Groups on the table: 3♣, 4♣, 5♣, 6♣ / Q♥, Q♦, Q♠ Player's hand: 5♣, 7♣, 8♣, Q♣, J♥, K♥ Player takes the 2 groups on the table, then he plays cards creating these new groups: 3♣, 4♣, 5♣ / 5♣, 6♣, 7♣, 8♣ / J♥, Q♥, K♥ / Q♦, Q♠, Q♣ * ''Restore'' If a player took cards from the table and he is no more able to play all token cards, he may force his turn ends by restoring the original game situation on the table and of his hand. He must put back the token cards on the table, restoring the original groups and collecting cards he had in his hand that he may have played. Once restored, player must draw 1 cards from the deck and pass. In case player is not able to restore, the other players will help him to do it and then he must draw 3 cards as penalty. NOTE WELL: In the BGA version cards token from the table are tagged with a red sign as reminder to play it before passing. '''The ''Restore'' action can be automatically performed clicking on a specific button and player receive 1 card from the deck'''. 3 cards penalty is not considered. * ''Pass'' Player can pass only if he played cards and if he has no cards taken from the table in his hand. == End of the game == Game ends as soon as a player has no more cards in his hand or the deck is depleted. In the second case, the winner is the player with less cards in his hand. ff889e5ec7853a9c7602e22ae96a6b0981540983 Gamehelptokaido 0 134 977 2013-10-11T13:39:13Z Nikos messis 4024 Created page with "- if you enter a panorama space,either pick that panorama or pick a cherry tree worth 2 points and 1 coin - if you enter a hot spring space,either pick the hot spring or pay 1..." wikitext text/x-wiki - if you enter a panorama space,either pick that panorama or pick a cherry tree worth 2 points and 1 coin - if you enter a hot spring space,either pick the hot spring or pay 1 coin to pick a bath house tile worth 4 points - if you enter a souvenir shop,either buy souvenir as usual or get 1 legendary item instead,there are 6 items of 3 kinds,type 1 is worth 1 point for every souvenir and cost 1 coin,type 2 cost 2 coin and count as a family of his own (so if you have a full set already it's worth 9 point) and type 3 give cost 3 coins and give you 8 points straight - if you enter a farm you can go instead to a gambling house,bet 2 coins and throw a dice... the dice is either 0x 1x 2x 3x or 4x so you either lose 2 coins,gain 0 coins,gain 2 coin,gain 4 coin or gain 6 coins - if you enter a temple you can pay 1 coin for an amulet of your choice,amulet effect include 1) if you get the lead,get another turn 2) get a free meal 3) get both choice when you enter a space 4) throw the gamble dice and get coin equal to the value 5) get to a space occupied by another player 6) coin spent on an item go to the temple instead (you still get the item) - if you enter an encounter space you can get a caligraphy item instead which scores point at the end of the game depending on how you play, these include 1) score 3 point for every skipped meal 2) score 2 point for every coin unspent you have in the end 3) score 3 point for every completed panorama and 1 point for every cherry tree 4) score 2 point per legendary item and 1 point for every souvenir 5) score 6 point if you are the last to arrive at the final inn,4 point if you are the second to last player and 2 point in the other case's 6) score 2 point per accomplishment and 1 point for every caligraphy item to complete it all there are 6 new travelers 1) the yakuza - 5 coins - everytime he arrives on one of the three inn he can bet a single coin to the gambling house 2) the gourmet - 2 coins - he scores point equal to the price of the meal he eats 3) the old lady - 4 coins - when she stops on an encounter space, she can get both the encounter AND the caligraphy item 4) the superstitious woman - 4 coins - when she stops on a temple space,she can both donate coin to the temple AND buy an amulet 5) the kid - 3 coins - when he arrives on one of the three inn he gets a free souvenir 6) the souvenir seller - 0 coins - when he stops at a panorama he gets one cointeasel Written by teasel 8ca5663754ea3bc364dde6dadccb5f7c673cc64f 981 977 2013-10-17T06:46:37Z Hofty 3828 wikitext text/x-wiki * if you enter a panorama space,either pick that panorama or pick a cherry tree worth 2 points and 1 coin * if you enter a hot spring space,either pick the hot spring or pay 1 coin to pick a bath house tile worth 4 points * if you enter a souvenir shop,either buy souvenir as usual or get 1 legendary item instead,there are 6 items of 3 kinds,type 1 is worth 1 point for every souvenir and cost 1 coin,type 2 cost 2 coin and count as a family of his own (so if you have a full set already it's worth 9 point) and type 3 give cost 3 coins and give you 8 points straight * if you enter a farm you can go instead to a gambling house,bet 2 coins and throw a dice... the dice is either 0x 1x 2x 3x or 4x so you either lose 2 coins,gain 0 coins,gain 2 coin,gain 4 coin or gain 6 coins * if you enter a temple you can pay 1 coin for an amulet of your choice,amulet effect include *# if you get the lead,get another turn *# get a free meal *# get both choice when you enter a space *# throw the gamble dice and get coin equal to the value *# get to a space occupied by another player *# coin spent on an item go to the temple instead (you still get the item) * if you enter an encounter space you can get a caligraphy item instead which scores point at the end of the game depending on how you play, these include *# score 3 point for every skipped meal *# score 2 point for every coin unspent you have in the end *# score 3 point for every completed panorama and 1 point for every cherry tree *# score 2 point per legendary item and 1 point for every souvenir *# score 6 point if you are the last to arrive at the final inn,4 point if you are the second to last player and 2 point in the other case's *# score 2 point per accomplishment and 1 point for every caligraphy item * to complete it all there are 6 new travelers *# the yakuza - 5 coins - everytime he arrives on one of the three inn he can bet a single coin to the gambling house *# the gourmet - 2 coins - he scores point equal to the price of the meal he eats *# the old lady - 4 coins - when she stops on an encounter space, she can get both the encounter AND the caligraphy item *# the superstitious woman - 4 coins - when she stops on a temple space,she can both donate coin to the temple AND buy an amulet *# the kid - 3 coins - when he arrives on one of the three inn he gets a free souvenir *# the souvenir seller - 0 coins - when he stops at a panorama he gets one coin a46e8ef670d710022e52d42cb42129d1c576b1b8 984 981 2013-10-18T17:43:11Z Hofty 3828 wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: : * Shokunin - one Souvenir : * Annaibito - one Panorama : * Samurai - 3 points : * Kuge - 3 coins : * Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. : * The first Souvenir in a set is worth 1 point. : * The second Souvenir in a set is worth 3 points. : * The third Souvenir in a set is worth 5 points. : * The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' : * A traveler cannot taste the same culinary specialty twice during his journey. : * A traveler can never purchase more than one Meal card per Inn. : * A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the achievement cards (Gourmet, Collector, Bather, Chatterbox) to the appropriate travelers. ---- '''(Extracted from the english rule book)''' f3bab9bef4594b16c6a61ddb881d33b21c87d290 997 984 2013-12-04T03:54:49Z Ched 4183 /* Fix up bulleted lists. */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the achievement cards (Gourmet, Collector, Bather, Chatterbox) to the appropriate travelers. ---- '''(Extracted from the english rule book)''' 9182c3f8aafc47e083dba6b17ff2727a22afddc5 998 997 2013-12-04T04:11:48Z Ched 4183 /* Added more information about the achievement cards. */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' e646fd159fcc1f219ba3be4e8385441df42ddaf9 Gamehelptzolkin 0 129 978 923 2013-10-15T22:12:51Z Firstbass 4055 /* Food Day */ wikitext text/x-wiki == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of temples from step you are on and all steps below -- ''End of Age (Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option, flip your board to dark side up. -- This does not avoid food days. 23abb93a403f6d112973adff7fc438db52a7ac31 About us 0 6 979 158 2013-10-16T05:57:12Z Wenleinotohou 4058 wikitext text/x-wiki First, we should say that we are gamers. Real, complete boardgames addicts. We designed '''Board Game Arena''' for players who can't play for real because of time, geographic or social constraints. We would like to provide a new game experience with the best of two worlds: video games and board games. Although we aim to provide high quality online games, we are still convinced that the best way to enjoy board games is around a *real* table with friends. This is one of the reason we strongly recommend you to buy physical copies of the games you discover on BGA. The other reason is simple to understand: each game sale is also a revenue for an author and an editor (... we've come full circle). [[Contact us]] 首先,我们认为自己是玩家。在现实生活中是从头彻尾的桌游爱好者。 由于时间,地域或者社区环境的原因有一些玩家不能在现实中游戏,这成为我们设计BGA的原因。我们希望借电脑游戏和桌面游戏这两者的精华,给玩家提供崭新的体验。 即使我们的目标是提供高质量的在线游戏,我们仍然相信享受桌游最好的方式是和朋友围在一张现实的桌子旁。这也是为什么我们建议您购买BGA上的桌游的实体版本。另一个原因很好理解,每份卖出的游戏可以给作者和编辑者带来收入。(...结果让我们回到了一开始的游戏方式) d7b0f542a6b2f75173e2553721b533b3750e192a Studio back-office 0 56 980 391 2013-10-16T08:54:43Z Sourisdudesert 1 wikitext text/x-wiki '''IMPORTANT''': the Studio back-office is now '''DEPRECATED'''. Most of its functionalities are now accessible from "Control Panel / Manage Games / Your games". You will find the URL to the Studio back-office in the 'resources.html' file at the root of your SFTP access. Here is a list and description of the links and functions in this back-office. Click on a menu to display its content. === Documentation === Contains links to this wiki, the development forum and the bugs forum. === Database === Contains a link to the Studio database administration tool (PHPMyAdmin). Your login / password for this tool is the same as for your SFTP access. ('''DEPRECATED''': now you can access to your game database with the link "Game Database" at the bottom of your game page). === Sources === Contains a form for committing your sources to the BGA repository. * Game name field should contain the name of your game (lower case, no space). Ex: puertorico * Comment field should contain your commit comment describing changes to the code since your last commit. You should commit from time to time when you hit some landmarks in your development. This is an extra assurance not to lose your code, and to have the possibility (by asking BGA administrators) to get a previous version of your code if you need to backtrack. ('''DEPRECATED''': now this function is available from your Control Panel). === Logs === Gives you a web based access to the studio server logs. ; Current table error log : main log of interest to you while developing, it contains the error happening at the table you are currently playing at. ('''DEPRECATED''': now you can access this log with the coreesponding link at the bottom of your game page). ; Current table request logs : this log traces all the actions happening at the table you are currently playing at. ('''DEPRECATED''': now you can access this log with the coreesponding link at the bottom of your game page). ; Javascript error log : this log traces all the Javascript errors happening on the client side. Errors are also briefly displayed in your browser, but in this log it's better formatted and you can look a it quietly. ('''DEPRECATED''': you should use your Javascript console / Firebug plugin to do this). ; Gameserver error log : this log traces all errors happening on the gameserver. It should mainly be useful if your game setup crashes (ie before your table is ready, so before errors are collected in the current table error log), for example if there is a syntax error in your 'dbmodel.sql' file. ('''DEPRECATED''': errors during the game setup appears now directly in a red error message during your "Express start"). ; HTTP error log : this log traces the web server errors. This is useful to look at PHP syntax errors and warnings. ('''DEPRECATED''': errors now appear directly in a red message during the game). Please note that the three last logs are common to all games being developed on the platform, so there may be some noise in the data you are interested in. You can open the log links in a different tab and just hit F5 when you need to refresh. The URL you will get for the new tab has a "?n=100" parameter that gives the number of lines to display: you can modify it in the URL if you need more. ffcb9ffc3be270bfd19b8f277d8d4d1c4029e27c Gamehelptimemasters 0 135 982 2013-10-17T17:40:54Z Sigmund3 4072 Time Masters Rules wikitext text/x-wiki Phases of the game: 1)Timer phase If at the beginning of the turn you have a full timer, you must choose to either: - activate it: in which case the timer and all the cards used to delay it are discarded. The spells on it remain in the Timer area and you must play them until the end of turn. - delay it: in which case you must put a card from your hand, with the card back showing, on that timer. If you have a card on a timer you will replenish your hand until you have one card less, such as 4 cards instead of 5. Two cards to delay the timer mean 3 card in your hand maximum and so on. If at the start of your turn you have a timer that is full, even if you pass the turn to replenish all your Ke, you must put a card from your hand to delay it. 2)Main phase: You may play Spells, Focus or Timers and also Spells that are on active timers. 3)Access spheres Phase You may take cards from the Spheres of Consciousness depending on the number of spells played during the turn. These new cards go directly in your hand. You can't choose to draw from the same sphere during the same turn. One spell and you can draw from the first level, two and you can draw from the second and so on. Starting with the third spell you can combine and choose, a first and second level spell. It is possible to take the second card from a level, which is not revealed. For this you must pay an extra cost. For example, to take the second card from the second level, you must play three spells. 4)Closing Phase Discard all your played spells and replenish your hand until you have 5 cards, or less if you have chosen to delay timers. End of game The game finishes when 2 of the 5 spheres are empty, or more exactly at the end of turn of the player who draws the card that makes it happen. The players score points such that a card of gives an amount of points equal to the level it belongs. In case of equality the player with the highest amount of Ke wins. f94cac307666332fc62671acdcba9b87c505bc77 983 982 2013-10-18T05:00:56Z Jamie ca 4076 wikitext text/x-wiki Phases of the game: 1)Timer phase If at the beginning of the turn you have a full timer, you must choose to either: - activate it: in which case the timer and all the cards used to delay it are discarded. The spells on it remain in the Timer area and you must play them until the end of turn. - delay it: in which case you must put a card from your hand, with the card back showing, on that timer. If you have a card on a timer you will replenish your hand until you have one card less, such as 4 cards instead of 5. Two cards to delay the timer mean 3 card in your hand maximum and so on. If at the start of your turn you have a timer that is full, even if you pass the turn to replenish all your Ke, you must put a card from your hand to delay it. 2)Main phase: You may play Spells, Focus or Timers and also Spells that are on active timers. 3)Access spheres Phase You may take cards from the Spheres of Consciousness depending on the number of spells played during the turn. These new cards go directly in your hand. You can't choose to draw from the same sphere during the same turn. One spell and you can draw from the first level, two and you can draw from the second and so on. Starting with the third spell you can combine and choose, a first and second level spell. It is possible to take the second card from a level, which is not revealed. For this you must pay an extra cost. For example, to take the second card from the second level, you must play three spells. 4)Closing Phase Discard all your played spells and replenish your hand until you have 5 cards, or less if you have chosen to delay timers. End of game The game finishes when 2 of the 5 spheres are empty, or more exactly at the end of turn of the player who draws the card that makes it happen. The players score points such that a card of gives an amount of points equal to the level it belongs. In case of equality the player with the highest amount of Ke wins. c5849fb07b0191de069bfcac949cda5e27d07595 Gamehelpchinagold 0 136 985 2013-10-19T12:15:21Z Elisha 2997 Created page with "Hi!" wikitext text/x-wiki Hi! c0a0ad26a634840c67a210fefdda76577b03a111 Game art: img directory 0 89 986 950 2013-10-20T20:23:41Z Sourisdudesert 1 /* Requested images */ wikitext text/x-wiki == Requested images == The following images are requested by BGA: ;game_box.png * It is displayed on the main site on the game description page and when creating a table (280x280 px). * It should be a 3D image of a physical copy of the game box as it appears in an online shop. * It is better to take the version of the game that is coherent with the game art used in the adaptation, and from the original publisher of the game. * The background of the image must be transparent. * If you don't have a 3D version of the game box, you can use the following website to create one: http://www.3d-pack.com/ ;game_icon.png * It is the icon displayed in the lists of games and tables (50x50 px). * This one should not be transparent, and shouldn't have a border (a black border will be add by BGA). * The objective of this icon is to make the game recognizable among the other games. A good idea is to take a part of the game cover that is distinctive (ex: the game title). ;publisher.png * It is the logo of the publisher of the game, displayed on the game description page. * The width must be 150 px. The height can be anything. The image could be transparent. ;publisher2.png (optional) * If the game has been co-published by 2 publishers, you should upload a second image named "publisher2.png" (same characteristic than the first one). '''Important''': when you modify these images, you MUST click on "Reload game box image" from the Control Panel in order your update can be taken into account. == Game art == You must upload in img directory all images of your game interface. === Images loading === '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. Note that you can tune the way images are loaded with Javascript method "dontPreloadImage" (see [[Game_interface_logic:_yourgamename.js|Game Interface Logic]]). === Images format === You can use 3 image format while building your game interface: ;jpg images should be used for non-transparent images. Jpg are usually lighter than Pngs, so please choose Jpg for big pictures (ex: game board, cards) when you don't need transparency to accelerate game load. ;png images should be used for transparent images. ;gif images can be used for animated images. This is not recommended to use gif animated images as they can upset players, but for some specific interface element this could be useful. === Use CSS Sprites === To limit the number of images load and make the game load faster, you must use CSS sprites, ie you must gather several images in a single one. To learn more on CSS Sprites: * [http://www.w3schools.com/css/css_image_sprites.asp CSS sprites (W3C documentation)]. * [[Game interface stylesheet: yourgamename.css]] a3689edf2cf06b2f1687bc4d17adaeaf8a23ff3b Game interface logic: yourgamename.js 0 88 988 902 2013-10-27T20:30:47Z Vaanatomic 2276 /* Players input */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 4cf3443c514462554d9e6824a05bc2c286383728 Gamehelphawaii 0 34 989 425 2013-11-05T16:44:26Z Uss wampanoag 4200 /* Game-End VP's: */ obvious error, kahunas instead of tikis wikitext text/x-wiki Overview: You are the chieftain of a kingdom (your placemat) with up to five villages (rows). You buy or go to the islands to get place tiles that you put in your villages. There are five rounds and you get a decreasing amount of resources each round with which to acquire place tiles. Its a many-paths-to-victory kind of game. (It has been described as Stone Age without the dice). There are three kinds of resources: 1) Feet - let you move around the board, or travel to the islands or go to the cove to catch fish. 2) Clam shells - let you buy place tiles from the board 3) Fruit - can be used in place of feet or clam shells in all-or-nothing fashion. If you substitute for feet, you must substitute the entire moving cost. If you substitute for clams, you must substitute the entire purchase price. Moving: Your chieftains start at the beach and it costs nothing to return there from the board. Each movement in from the beach costs one foot. Moving up, down, right, left or diagonal on the board costs one foot per space. Staying on a space costs one foot. To go to the islands, you use your boats and feet. The closest island costs three feet and the farthest costs six. You have to have the feet as well as the space on the boats for the feet. You get an immediate victory point bonus for going to the islands but you do not get a price token (more about that later). Also, you get the level 2 version of whatever place tile or item you are getting on the islands. For instance, to go to the six foot island, you would need either: A) two three-feet boats and six feet resources or B) you would need the four-foot boat (which comes with a prepaid foot) and the two-foot boat as well as five feet resources. That is the advantage of the pre-paid foot on the four-foot boat. == Purchasing: == To buy some thing from the board: Select the price token (round colored circle with number inside) on the space with the place tile or item you want. If there is more than one place tile available on that space, you will be given a choice of which one you want. The movement cost will be in feet and the purchase price will be in clam shells but you can substitute either or both with fruit if you have it. You gain the price token for the round. There must be an available price token in order to purchase something from that space. You can pay double to get the level 2 version, which for most tiles is double effect. You place it in the village (row) of your choice. You must be able to place it in order to purchase it. A) You can only have one of each type of place tile in each village. For instance, you cannot have a foot (1) hut and a foot (2) hut in the same village but you can have a coconut fruit (1) tree and a breadfruit (2) tree in the same village. B) You can have only one god in each village. You can only have one of each type of god in your whole kingdom and the god vp effects are kingdom-wide. B) Each village must begin with a hut. The huts have a picture of a hut on them: foot hut, clam hut, spear hut, exchange hut, and long hut. ''' == Resources == ''' == Base reources == Base resources are on the round indicator. They decrease with each succeeding round. Everyone gets the base resources at the beginning of the round. Any unused resources can be used in the next round. == In-play resource bonuses == *The third, fourth and fifth villages give you 1, 2 and 3 clams respectively when you purchase Kahunas (Hawaiian priests) for them. *When you purchase for your kingdom the second, third and fourth Tikis, you are granted 1, 2, and 3 feet, respectively. *One or two of the island treasures are four fruit resources. == Resource Generators: == generate resources at the beginning of the next round. *Fruit level ones - one fruit *Fruit level twos - two fruit *Clam hut level one - one clam *Clam hut level two - two clams *Foot hut level one - one foot *Foot hut level two - two feet *Irrigation level two - one resource of your choice *Ku god - level one and two - one foot *Kane god level one - one clam *Kane god level two - two clams == Converter huts == Whenever you spend resources, a level 1 converter hut allows you to substitute one resource of your choice. So, for instance, you could move one space and buy something that has four price token on it with four fruit for the token and one clamshell being substituted for the one foot movement. Another example, you could go to the first island for two feet and one clamshell using a level 1 boat. The converter level 2 hut is the same but with any two resources. '''== Victory Points ==''' == In-play VP's: == *Going to the islands gives you a vp bonus. *Some of the treasures on the islands are 5 vp's. *Spear huts and the Ku god give you a bonus whenever you take a price token with crossed spears. Level 1 is 1 pt. bonus, Level 2 is two pt. bonus. == Round-End VP's: == There is a large sheild on the round indicator at the bottom. If the sum of price tokens and fish are greater than the number on the sheild, then you get a bonus. There is a large bonus for the player with highest sum score, a moderate bonus for the second highest and a token bonus for everyone else whose sum was greater than the sheild. Surfer place tiles lower the sheild value for qualifying for the bonus but surfers do not contribute to your sum when comparing to other players. Lono god level 1 gives you 2 vp's if you qualify for the bonus, and Lono god level 2 gives you 4 vp's if you qualify for the bonus == Game-End VP's: == A) Each village whose size does not extend to the Tikis (rightmost place tile of a row must intersect with the leftmost column of a Tiki) is ignored for end-game scoring - its place tiles do not count and neither does its Kahuna. The villages below it are not affected - each village must reach the leftmost Tiki independently. B) The Kahunas on the first, second, third, fourth and fifth villages scores the player five, five, ten, ten and fifteen points respectively. B) A hula dancer in a village scores one point for each place tile in her village including herself. A level 2 hula dancer scores two points instead of one for each place tile in her village including herself. C) Irrigation gives you pts for each type of fruit you have in that particular village: Breadfruit, guava, coconuts and bananas. D) The Long Hut level 2 is worth five points. The long hut level one just takes up space making it easier to reach the Kahunas but does not, itself, score points. E) The Laka god in your kingdom scores one pt for each fruit symbol on your place tiles in all of the villages which is the whole kingdom. Laka god level 2 scores two points for each fruit symbol. F) Kanaloa god scores two pts for each boat and each surfer on all the villages in the kingdom. Kanaloa 2 scores 4 pts for each boat and surfer. dd6fa6c5e9dd532aa9d7d11b65d0b8fbcfcc2eeb Gamehelpjumpgate 0 137 990 2013-11-09T07:08:53Z Chazm 3718 Initilal reference card info wikitext text/x-wiki Jump Gate == Turn Reference == Take two actions on your turn. You may take the same action twice. Fly ... to a neighboring planet. Jump ... to another more distant planet. Scan ... your current planet if it has any face-down resources. Land & Claim ... your current planet if it has no face-down resources and is unclaimed. Harvest ... 1 reource you marked with a Scan, or 1 unmarked resource at a planet with all face-up resources. Research ... refill your hand up to 5 cards. You may discard any number of cards before refilling. == Scoring Referece == If there are 7 or more markers on the Black Hole, the player(s) with the most markers on the Black Hole must discard their hidden resource cards before scoring. Otherwise, players reveal their hidden resource cards and add them to their collected resources for scoring. Score all collected resources as explained on the resource cards. Add 4 points for each planet claimed. Player with the highest score wins. c12f202940a60092b5a498dff6f39da8eaf83ca7 Gamehelparmadora 0 138 992 2013-11-12T22:15:04Z Luckily 4242 Created page with "Nuggets Winning Moves, 2003 designed by Christwart Conrad translated by Pitt Crandlemire Whoever digs others a goldmine... For 2 - 4 players from 8 years Gam..." wikitext text/x-wiki Nuggets Winning Moves, 2003 designed by Christwart Conrad translated by Pitt Crandlemire Whoever digs others a goldmine... For 2 - 4 players from 8 years Game material - 1 gameboard consisting of a 5x8 grid - 35 blockers - 8 gold mine markers showing the number of goldstrike points for that area - 4x16 chips with different numbers - 1 rules booklet Idea of the game Nuggets have been found in the Golden Valley! The gold diggers try to sell their claims to the big mining companies for as much as they can get by gathering as many Nuggets as possible. The players take on the role of prospectors and try through skillful placement of blockers to create valuable claims and then to control them with their high value chips. But the other players have not fallen on their heads and will compete for those claims quite lively, too If it all comes to naught, the claims are laid out once again. In the end, the one who walks away with the most money will be the one who bought up the most productive claims. Set-up The numbered stickers are stuck onto the gold mine markers and these are then placed on the board (see the illustration). Each player chooses a color and takes as many chips of that color in the appropriate value as indicated on the following table. He places these face-down in front of himself. In a 4-player game, players sitting opposite each other form a team. Chip-distribution total 1 point 2 points 3 points 4 points 5 points 2 players, each 16 11 2 1 1 1 3 players, each 11 7 2 1 1 - 4 players, each 8 5 1 1 1 - The Gold mine markers are placed on the gameboard as follows: (refer to illustration in German rules) The youngest player begins the first game. In future games, the player that lost the most recent game always starts. Course of the game Play goes clockwise. On his turn, a player must execute one of the following actions: EITHER build two blockers OR place a chip on an unoccupied space Build two blockers In each case, the player places a blocker on any one unoccupied line between 2 spaces. The blockers need not be placed adjacent to each other. Restriction: a player may not place a blocker such that an area of less than 4 spaces is completely enclosed. Note: it is possible that there may be several gold mine markers in an area or even none at all. Place a chip The player places one of his colored chips face-down on any one unoccupied space (not already containing a chip or a gold mine marker). He secretly chooses which of his numbered chips he wishes to place. Players should decide at the beginning of the game whether players will be allowed to look at their own chips after they've been played or whether they'll simply be required to remember the numbers after they've been played.. Partnership game Partners may communicate with each other in the 4-player game but they may not do so privately; the players on the other team must be able to see and hear everything. Also, partners may not show chips to each other. End of the game If a player has no more chips, he may still play blockers. Also, if there are no more blockers, a player may play his chips. Whenever a player can't or chooses not to play, he may pass but he may not then play again later. Once the last player has passed, the game ends. Scoring All chips are turned over. For each enclosed area, each player sums the points on his chips. The points of both partners are added together in the team game. The player with most points in an area gets all the gold mine markers in that area which score points equal to their value. Areas without gold mine markers score nothing. In the event of a tie in an area, the number of goldstrike points on the marker is divided among the tied players. (Half points are awarded when appropriate.) Whoever ultimately has acquired the most goldstrike points is able to mine the most gold Nuggets and wins the game. Variant Once you have become familiar with the game played according to the standard set-up, you can then try placing the gold mine markers differently in later games. 1e3776191a34e20407db27058f671a61b4d3281d 994 992 2013-11-29T05:32:37Z Stealthswor 3331 wikitext text/x-wiki '''Nuggets:''' Published by Winning Moves, 2003 - designed by Christwart Conrad - translated by Pitt Crandlemire '''Players''': For 2 - 4 players from 8 years '''Game material''': 1 gameboard consisting of a 5x8 grid, 35 blockers, 8 gold mine markers showing the number of goldstrike points for that area, 4x16 chips with different numbers, 1 rules booklet '''Idea of the game''': Nuggets have been found in the Golden Valley! The gold diggers try to sell their claims to the big mining companies for as much as they can get by gathering as many Nuggets as possible. The players take on the role of prospectors and try through skillful placement of blockers to create valuable claims and then to control them with their high value chips. But the other players have not fallen on their heads and will compete for those claims quite lively, too If it all comes to naught, the claims are laid out once again. In the end, the one who walks away with the most money will be the one who bought up the most productive claims. '''Set-up''': The numbered stickers are stuck onto the gold mine markers and these are then placed on the board (see the illustration). Each player chooses a color and takes as many chips of that color in the appropriate value as indicated on the following table. He places these face-down in front of himself. In a 4-player game, players sitting opposite each other form a team. '''Chip-distribution''': 2 players, each 16 11 2 1 1 1 3 players, each 11 7 2 1 1 - 4 players, each 8 5 1 1 1 - The Gold mine markers are placed on the gameboard as follows: (refer to illustration in German rules) The youngest player begins the first game. In future games, the player that lost the most recent game always starts. '''Course of the game''' Play goes clockwise. On his turn, a player must execute one of the following actions: ''EITHER'' build two blockers ''OR'' place a chip on an unoccupied space '''Build two blockers''': In each case, the player places a blocker on any one unoccupied line between 2 spaces. The blockers need not be placed adjacent to each other. Restriction: a player may not place a blocker such that an area of less than 4 spaces is completely enclosed. Note: it is possible that there may be several gold mine markers in an area or even none at all. '''Place a chip''': The player places one of his colored chips face-down on any one unoccupied space (not already containing a chip or a gold mine marker). He secretly chooses which of his numbered chips he wishes to place. Players should decide at the beginning of the game whether players will be allowed to look at their own chips after they've been played or whether they'll simply be required to remember the numbers after they've been played.. '''Partnership game''': Partners may communicate with each other in the 4-player game but they may not do so privately; the players on the other team must be able to see and hear everything. Also, partners may not show chips to each other. '''End of the game''': If a player has no more chips, he may still play blockers. Also, if there are no more blockers, a player may play his chips. Whenever a player can't or chooses not to play, he may pass but he may not then play again later. Once the last player has passed, the game ends. '''Scoring''': All chips are turned over. For each enclosed area, each player sums the points on his chips. The points of both partners are added together in the team game. The player with most points in an area gets all the gold mine markers in that area which score points equal to their value. Areas without gold mine markers score nothing. In the event of a tie in an area, the number of goldstrike points on the marker is divided among the tied players. (Half points are awarded when appropriate.) Whoever ultimately has acquired the most goldstrike points is able to mine the most gold Nuggets and wins the game. 51ef95683b6ec3869cac679e26a50fcba8afe0e2 995 994 2013-11-29T15:32:37Z Trzynastka 4332 wikitext text/x-wiki *********************************************************************************************************************************** '''''Game Nuggets is not the same as Armadora, it is only similar.''''' Proper rules: http://pl.boardgamearena.com/link?url=http%3A%2F%2Fcdn.boardgamearena.net%2Fdata%2Fnewsimg%2FArmadora_EN.pdf&id=1228 *********************************************************************************************************************************** '''Nuggets:''' Published by Winning Moves, 2003 - designed by Christwart Conrad - translated by Pitt Crandlemire '''Players''': For 2 - 4 players from 8 years '''Game material''': 1 gameboard consisting of a 5x8 grid, 35 blockers, 8 gold mine markers showing the number of goldstrike points for that area, 4x16 chips with different numbers, 1 rules booklet '''Idea of the game''': Nuggets have been found in the Golden Valley! The gold diggers try to sell their claims to the big mining companies for as much as they can get by gathering as many Nuggets as possible. The players take on the role of prospectors and try through skillful placement of blockers to create valuable claims and then to control them with their high value chips. But the other players have not fallen on their heads and will compete for those claims quite lively, too If it all comes to naught, the claims are laid out once again. In the end, the one who walks away with the most money will be the one who bought up the most productive claims. '''Set-up''': The numbered stickers are stuck onto the gold mine markers and these are then placed on the board (see the illustration). Each player chooses a color and takes as many chips of that color in the appropriate value as indicated on the following table. He places these face-down in front of himself. In a 4-player game, players sitting opposite each other form a team. '''Chip-distribution''': 2 players, each 16 11 2 1 1 1 3 players, each 11 7 2 1 1 - 4 players, each 8 5 1 1 1 - The Gold mine markers are placed on the gameboard as follows: (refer to illustration in German rules) The youngest player begins the first game. In future games, the player that lost the most recent game always starts. '''Course of the game''' Play goes clockwise. On his turn, a player must execute one of the following actions: ''EITHER'' build two blockers ''OR'' place a chip on an unoccupied space '''Build two blockers''': In each case, the player places a blocker on any one unoccupied line between 2 spaces. The blockers need not be placed adjacent to each other. Restriction: a player may not place a blocker such that an area of less than 4 spaces is completely enclosed. Note: it is possible that there may be several gold mine markers in an area or even none at all. '''Place a chip''': The player places one of his colored chips face-down on any one unoccupied space (not already containing a chip or a gold mine marker). He secretly chooses which of his numbered chips he wishes to place. Players should decide at the beginning of the game whether players will be allowed to look at their own chips after they've been played or whether they'll simply be required to remember the numbers after they've been played.. '''Partnership game''': Partners may communicate with each other in the 4-player game but they may not do so privately; the players on the other team must be able to see and hear everything. Also, partners may not show chips to each other. '''End of the game''': If a player has no more chips, he may still play blockers. Also, if there are no more blockers, a player may play his chips. Whenever a player can't or chooses not to play, he may pass but he may not then play again later. Once the last player has passed, the game ends. '''Scoring''': All chips are turned over. For each enclosed area, each player sums the points on his chips. The points of both partners are added together in the team game. The player with most points in an area gets all the gold mine markers in that area which score points equal to their value. Areas without gold mine markers score nothing. In the event of a tie in an area, the number of goldstrike points on the marker is divided among the tied players. (Half points are awarded when appropriate.) Whoever ultimately has acquired the most goldstrike points is able to mine the most gold Nuggets and wins the game. 00f0954786d3db52bede4c38c5d0ea55e2855d8b 999 995 2013-12-05T14:18:23Z Trzynastka 4332 wikitext text/x-wiki * '''''Game Nuggets is not the same as Armadora, it is only similar.''''' Proper rules: http://pl.boardgamearena.com/link?url=http%3A%2F%2Fcdn.boardgamearena.net%2Fdata%2Fnewsimg%2FArmadora_EN.pdf&id=1228 * '''Nuggets:''' Published by Winning Moves, 2003 - designed by Christwart Conrad - translated by Pitt Crandlemire '''Players''': For 2 - 4 players from 8 years '''Game material''': 1 gameboard consisting of a 5x8 grid, 35 blockers, 8 gold mine markers showing the number of goldstrike points for that area, 4x16 chips with different numbers, 1 rules booklet '''Idea of the game''': Nuggets have been found in the Golden Valley! The gold diggers try to sell their claims to the big mining companies for as much as they can get by gathering as many Nuggets as possible. The players take on the role of prospectors and try through skillful placement of blockers to create valuable claims and then to control them with their high value chips. But the other players have not fallen on their heads and will compete for those claims quite lively, too If it all comes to naught, the claims are laid out once again. In the end, the one who walks away with the most money will be the one who bought up the most productive claims. '''Set-up''': The numbered stickers are stuck onto the gold mine markers and these are then placed on the board (see the illustration). Each player chooses a color and takes as many chips of that color in the appropriate value as indicated on the following table. He places these face-down in front of himself. In a 4-player game, players sitting opposite each other form a team. '''Chip-distribution''': 2 players, each 16 11 2 1 1 1 3 players, each 11 7 2 1 1 - 4 players, each 8 5 1 1 1 - The Gold mine markers are placed on the gameboard as follows: (refer to illustration in German rules) The youngest player begins the first game. In future games, the player that lost the most recent game always starts. '''Course of the game''' Play goes clockwise. On his turn, a player must execute one of the following actions: ''EITHER'' build two blockers ''OR'' place a chip on an unoccupied space '''Build two blockers''': In each case, the player places a blocker on any one unoccupied line between 2 spaces. The blockers need not be placed adjacent to each other. Restriction: a player may not place a blocker such that an area of less than 4 spaces is completely enclosed. Note: it is possible that there may be several gold mine markers in an area or even none at all. '''Place a chip''': The player places one of his colored chips face-down on any one unoccupied space (not already containing a chip or a gold mine marker). He secretly chooses which of his numbered chips he wishes to place. Players should decide at the beginning of the game whether players will be allowed to look at their own chips after they've been played or whether they'll simply be required to remember the numbers after they've been played.. '''Partnership game''': Partners may communicate with each other in the 4-player game but they may not do so privately; the players on the other team must be able to see and hear everything. Also, partners may not show chips to each other. '''End of the game''': If a player has no more chips, he may still play blockers. Also, if there are no more blockers, a player may play his chips. Whenever a player can't or chooses not to play, he may pass but he may not then play again later. Once the last player has passed, the game ends. '''Scoring''': All chips are turned over. For each enclosed area, each player sums the points on his chips. The points of both partners are added together in the team game. The player with most points in an area gets all the gold mine markers in that area which score points equal to their value. Areas without gold mine markers score nothing. In the event of a tie in an area, the number of goldstrike points on the marker is divided among the tied players. (Half points are awarded when appropriate.) Whoever ultimately has acquired the most goldstrike points is able to mine the most gold Nuggets and wins the game. 0fd5e3724a47ed7d81f1689cd82c622ee19514e3 Contact us 0 9 993 139 2013-11-27T21:59:46Z Sourisdudesert 1 wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive '''a lot''' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the [http://boardgamearena.com/#!bugs bug reporting system] * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Postal address == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 980a98bd2ca063ac351422c896d84cf8920c62a0 996 993 2013-12-03T13:35:26Z Januszk0 3262 wikitext text/x-wiki '''Board Game Arena''' znajduje się we Francji. == Kontakt e-mail == contact(at)boardgamearena.com Otrzymujemy '''wiele''' e-maili. Prosimy nie wysyłać do nas e-mail w każdym z tych dwóch przypadków: * Jeśli chcesz zgłosić błąd, proszę zrobić to poprzez [http://boardgamearena.com/#!bugs System zgłaszania błędów] * Jeśli chcesz zgłosić gracza naruszającego zasady BGA, użyj przycisku "Zgłoś tego gracza" na jego/jej profilu. == Adres pocztowy == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 77f301b40cd095e78da87dc00bf3664ef39f8b38 Gamehelphex 0 139 1000 2013-12-07T04:58:53Z Kennyyy 4327 Created page with "====GOAL==== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ====OPENING==== The first player..." wikitext text/x-wiki ====GOAL==== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ====OPENING==== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player plays another token with his new color. ====TURNS==== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ====GAME ENDS==== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ====VARIANTS==== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 64c70a6a7b2646cee825432cab9587a99814c548 1001 1000 2013-12-07T04:59:35Z Kennyyy 4327 wikitext text/x-wiki ====GOAL==== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ====OPENING==== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player plays another token with his new color. ====TURNS==== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ====GAME ENDS==== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ====VARIANTS==== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 38a20c50a816bc26f1dbc09c03711a1b96b2cb44 Gamehelphex 0 139 1002 1001 2013-12-07T05:00:27Z Kennyyy 4327 wikitext text/x-wiki ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player plays another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 0dbe66a3ee55bc0f448f3c6122e6f0e108827115 1003 1002 2013-12-07T05:21:26Z Kennyyy 4327 wikitext text/x-wiki The Hex game is a connection game independently invented by two mathematicians, Piet Hein and John Nash, respectively in 1942 and 1947. The interest of this game mainly lies on the contrast between the simplicity of its rules and the potential complexity of its strategy. Another interesting aspect is that the game can't end in a tie. ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player places another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 0c861842b852f6b5bcc5c19b2755d32b13bda7da 1006 1003 2013-12-07T05:23:50Z Kennyyy 4327 wikitext text/x-wiki ===INTRODUCTION=== The Hex game is a connection game independently invented by two mathematicians, Piet Hein and John Nash, respectively in 1942 and 1947. The interest of this game mainly lies on the contrast between the simplicity of its rules and the potential complexity of its strategy. Another interesting aspect is that the game can't end in a tie. ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player places another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 21e638b436f93d68a93d5e59dbeea8b6778508a3 1007 1006 2013-12-07T05:26:21Z Kennyyy 4327 wikitext text/x-wiki ===INTRODUCTION=== The Hex game is a connection game independently invented by two mathematicians, Piet Hein and John Nash, respectively in 1942 and 1947. The interest of this game mainly lies on the contrast between the simplicity of its rules and the potential complexity of its strategy. Another interesting aspect is that the game can't end in a tie. ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player places another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. 67fcdd553674042e6c4fa1d29f0f0d75af0c10d8 1008 1007 2013-12-07T05:27:17Z Kennyyy 4327 wikitext text/x-wiki ===INTRODUCTION=== The Hex game is a connection game independently invented by two mathematicians, Piet Hein and John Nash, respectively in 1942 and 1947. The interest of the game mainly lies on the contrast between the simplicity of its rules and the potential complexity of its strategy. Another interesting aspect is that the game can't end in a tie. ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player places another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. beddcbb667b6116e9af68afd6a17ecd86e092730 1014 1008 2013-12-09T14:15:39Z Kennyyy 4327 wikitext text/x-wiki ===INTRODUCTION=== Le jeu de Hex est un jeu de placement inventé indépendamment par deux mathématiciens, Piet Hein et John Nash, respectivement en 1942 et 1947. L’intérêt principal du jeu tiens a l'opposition entre la simplicité des règles et la complexité des stratégies mises en jeu. Qui plus est, le jeu ne peut finir en match nul, ce qui lui confère un autre aspect intéressant. ===OBJECTIF=== Pour chacun des joueurs le but du jeu est de créer une ligne de pions de sa couleur reliant ses deux cotes du plateau (de sa propre couleur également). ===OUVERTURE=== Le premier joueur joue un premier pion sur n'importe quelle case du plateau. Le second joueur a alors le choix entre: *continuer a jouer avec sa couleur sur n'importe quelle autre case du plateau; *ou décider d’échanger sa couleur avec son adversaire et de prendre le premier pion placé pour lui. Dans ce cas le premier joueur prend le prochain tour. ===TOURS=== Apres les deux premiers tours (voir OUVERTURE), chaque joueur joue un pion de sa couleur a son tour. Un pion peut-être placé sur n'importe quelle case libre du plateau. ===FIN=== Le jeu s’arrête lorsqu'un des joueur a créé une ligne continue antre ses deux cotes du plateau. Il ne peut y avoir d’égalité ou de jeu nul. ===VARIANTES=== Le jeu peut se jouer sur différentes tailles de plateau. Le 6x6 est a réserver aux débutant et permet d'apprendre facilement les règles et de repérer les premiers "motifs" de jeu. Le 11x11 est une taille classiquement jouée. Le 14x14 est la taille recommandée par John Nash, un des inventeur du jeu, tandis que le 15x15 permet des parties plus longues et potentiellement plus stratégiques. 6eb5ecead3656178d85c661827efabd26238db88 1016 1014 2013-12-11T07:05:32Z Trociu 3428 wikitext text/x-wiki ==Zasady gry== Gra toczy się na planszy w kształcie rombu, złożonej z sześciokątnych pól. Rozmiar planszy może się zmieniać, ale liczba pól na każdym z boków jest zawsze stała. Tradycyjnie pojedynki rozgrywa się na planszy 11×11. Każdy z dwóch graczy biorących udział w grze dysponuje kamieniami o odmiennych kolorach. Gracze na przemian układają kamienie na wolnych polach planszy, tak, aby sąsiadujące utworzyły nieprzerwany ciąg łączący boki planszy własnego koloru. Wygrywa ten z graczy, który ułoży taki ciąg jako pierwszy. Często stosuje się dodatkową zasadę, mówiącą, że gracz wykonujący ruch jako drugi zamiast położyć swój kamień na wolnym polu może 'przejąć' kamień przeciwnika, tzn. zamienić jego kamień na swój. Dotyczy to tylko drugiego ruchu w rozgrywce (pierwszego ruchu drugiego gracza). Ta zasada stosowana jest również tutaj. b370555bab4642ec2ba6750e7dbe8afa6ab09e24 1017 1016 2013-12-11T10:51:12Z Sourisdudesert 1 Reverted edits by [[Special:Contributions/Trociu|Trociu]] ([[User talk:Trociu|talk]]) to last revision by [[User:Kennyyy|Kennyyy]] wikitext text/x-wiki ===INTRODUCTION=== Le jeu de Hex est un jeu de placement inventé indépendamment par deux mathématiciens, Piet Hein et John Nash, respectivement en 1942 et 1947. L’intérêt principal du jeu tiens a l'opposition entre la simplicité des règles et la complexité des stratégies mises en jeu. Qui plus est, le jeu ne peut finir en match nul, ce qui lui confère un autre aspect intéressant. ===OBJECTIF=== Pour chacun des joueurs le but du jeu est de créer une ligne de pions de sa couleur reliant ses deux cotes du plateau (de sa propre couleur également). ===OUVERTURE=== Le premier joueur joue un premier pion sur n'importe quelle case du plateau. Le second joueur a alors le choix entre: *continuer a jouer avec sa couleur sur n'importe quelle autre case du plateau; *ou décider d’échanger sa couleur avec son adversaire et de prendre le premier pion placé pour lui. Dans ce cas le premier joueur prend le prochain tour. ===TOURS=== Apres les deux premiers tours (voir OUVERTURE), chaque joueur joue un pion de sa couleur a son tour. Un pion peut-être placé sur n'importe quelle case libre du plateau. ===FIN=== Le jeu s’arrête lorsqu'un des joueur a créé une ligne continue antre ses deux cotes du plateau. Il ne peut y avoir d’égalité ou de jeu nul. ===VARIANTES=== Le jeu peut se jouer sur différentes tailles de plateau. Le 6x6 est a réserver aux débutant et permet d'apprendre facilement les règles et de repérer les premiers "motifs" de jeu. Le 11x11 est une taille classiquement jouée. Le 14x14 est la taille recommandée par John Nash, un des inventeur du jeu, tandis que le 15x15 permet des parties plus longues et potentiellement plus stratégiques. 6eb5ecead3656178d85c661827efabd26238db88 1018 1017 2013-12-11T10:52:01Z Sourisdudesert 1 wikitext text/x-wiki ===INTRODUCTION=== The Hex game is a connection game independently invented by two mathematicians, Piet Hein and John Nash, respectively in 1942 and 1947. The interest of the game mainly lies on the contrast between the simplicity of its rules and the potential complexity of its strategy. Another interesting aspect is that the game can't end in a tie. ===GOAL=== The goal for each player is to create a connected path of his tokens linking his two opposite sides of the board (of his color). ===OPENING=== The first player places a first token on any cell of the board. The second player has then the choice between: *continue playing his current color (and thus place a second token anywhere on the board); *or decide to switch colors and keep the first move for him. In this case the first player places another token with his new color. ===TURNS=== After the first two moves (see OPENING), each player plays a token of his color on his turn. A token can be placed on any free cell of the board. ===GAME ENDS=== The game ends when one of the players has built a complete path with his tokens between his two sides of the board. The game can't end in a tie. ===VARIANTS=== The game can be played on several different board sizes. The 6x6 board is designed to help beginners to understand the basic rules and discover simple game patterns. The 11x11 board is a more classic size. The 14x14 board is the one recommended by John Nash, one of the game inventor, and the 15x15 allowed longer plays. beddcbb667b6116e9af68afd6a17ecd86e092730 Gamehelpquarto 0 140 1004 2013-12-07T05:21:34Z Sooz 4387 Created page with "Quarto is a simple abstract game that should take about 15 mins. to play. '''To win, complete a row of 4 pieces (horizontally, vertically, or diagonally) that share at least ..." wikitext text/x-wiki Quarto is a simple abstract game that should take about 15 mins. to play. '''To win, complete a row of 4 pieces (horizontally, vertically, or diagonally) that share at least one trait in common'''. The twist to the game is that you select the piece your opponent places on the board and vice versa. Pieces have 4 traits each which are a combination of the following: - Tall or Short - Dark or Light - Round or Square - Hollow or Solid 06255b5886333cb044a2c09873e72bf4d87658d4 1005 1004 2013-12-07T05:23:08Z Sooz 4387 wikitext text/x-wiki Quarto is a simple abstract game that should take about 15 mins. to play. '''To win, complete a row of 4 pieces (horizontally, vertically, or diagonally) that share at least one trait in common. The twist to the game is that you select the piece your opponent places on the board and vice versa.''' Pieces have 4 traits each which are a combination of the following: Tall or Short Dark or Light Round or Square Hollow or Solid f460fb16a46d83350325492ecd055c402e54c9a8 Game interface logic: yourgamename.js 0 88 1009 988 2013-12-08T02:50:31Z Kennyyy 4327 Specification about image preloading: don't need to specify for image boxes wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. '''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> bcb948111c1d9401116fc8dcab816d616cd5b6d4 Gamehelpthejellymonsterlab 0 141 1010 2013-12-09T13:06:02Z Adela82 4198 Created page with "In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you nee..." wikitext text/x-wiki In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you need to make your monsters. At the beginning, every player starts with two experiments. Every experiment shows a monster made with a colored body (blue, red, pink or gren), eyes (1, 2 or 3) and a mouth (happy or sad) and a lab where you must make it. The parts are spread out over the six labs, so you move to them, and grab the parts you need to make your monsters, but you cannot have more than 5 cards in your hand. Once you have your experiment card and the parts, move to the lab indicated by the experiment card and leave all there (the experiment card and the parts). When other player come to visit that lab, it'll find your monster and then, it will be yours. While you're moving through the labs, you're leaving trails in order to know which player has been the last visiting it. If you move to a lab with a trail, there may be a monster of the owner of the trail. In addition, you can't move to a lab you were the last visit, so you need to program your moves to have all the parts of the experiment before you arrive to the lab where you must leave it with all the parts in your hand. In any time, you may want to go to the center to pick up a new experiment and make new monsters. But you'll not be able to go to the center if you just came from there (your meeple shows laid down) and if you have the maximum cards in hand (five). When a player has 5 monsters done, the center is open to close the game. Any player who goes there, finishes the game. Every monster has an score on it, depending on how difficult is to find their parts. You can see how many parts are in the game of each type, and what are all the experiments in the guide card placed on the bottom left corner. The player with the most score points win the game. Advices: If you have experiments, one side too low scored, or on the other side too difficult to find the parts, you can leave the expèriment card in any lab, so other players can play with it, or maybe you can play with it in a later visit. ea8f82efbfe9ed3393312c857edc04e17a832170 1011 1010 2013-12-09T13:06:11Z Adela82 4198 wikitext text/x-wiki In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you need to make your monsters. At the beginning, every player starts with two experiments. Every experiment shows a monster made with a colored body (blue, red, pink or gren), eyes (1, 2 or 3) and a mouth (happy or sad) and a lab where you must make it. The parts are spread out over the six labs, so you move to them, and grab the parts you need to make your monsters, but you cannot have more than 5 cards in your hand. Once you have your experiment card and the parts, move to the lab indicated by the experiment card and leave all there (the experiment card and the parts). When other player come to visit that lab, it'll find your monster and then, it will be yours. While you're moving through the labs, you're leaving trails in order to know which player has been the last visiting it. If you move to a lab with a trail, there may be a monster of the owner of the trail. In addition, you can't move to a lab you were the last visit, so you need to program your moves to have all the parts of the experiment before you arrive to the lab where you must leave it with all the parts in your hand. In any time, you may want to go to the center to pick up a new experiment and make new monsters. But you'll not be able to go to the center if you just came from there (your meeple shows laid down) and if you have the maximum cards in hand (five). When a player has 5 monsters done, the center is open to close the game. Any player who goes there, finishes the game. Every monster has an score on it, depending on how difficult is to find their parts. You can see how many parts are in the game of each type, and what are all the experiments in the guide card placed on the bottom left corner. The player with the most score points win the game. Advices: If you have experiments, one side too low scored, or on the other side too difficult to find the parts, you can leave the expèriment card in any lab, so other players can play with it, or maybe you can play with it in a later visit. e3493b9fd52feb5d855a879718adb71b39011ae6 1012 1011 2013-12-09T13:12:21Z Adela82 4198 wikitext text/x-wiki In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you need to make your monsters. At the beginning, every player starts with two experiments. Every experiment shows a monster made with a colored body (blue, red, pink or gren), eyes (1, 2 or 3) and a mouth (happy or sad) and a lab where you must make it. The parts are spread out over the six labs, so you move to them, and grab the parts you need to make your monsters, but you cannot have more than 5 cards in your hand. Once you have your experiment card and the parts, move to the lab indicated by the experiment card and leave all there (the experiment card and the parts). When other player come to visit that lab, it'll find your monster and then, it will be yours. While you're moving through the labs, you're leaving trails in order to know which player has been the last visiting it. If you move to a lab with a trail, there may be a monster of the owner of the trail. In addition, you can't move to a lab you were the last visit, so you need to program your moves to have all the parts of the experiment before you arrive to the lab where you must leave it with all the parts in your hand. In any time, you may want to go to the center to pick up a new experiment and make new monsters. But you'll not be able to go to the center if you just came from there (your meeple shows laid down) and if you have the maximum cards in hand (five). When a player has 4 monsters done, the center is open to close the game. Any player who goes there, finishes the game. Every monster has an score on it, depending on how difficult is to find their parts. You can see how many parts are in the game of each type, and what are all the experiments in the guide card placed on the bottom left corner. The player with the most score points win the game. Advices: If you have experiments, one side too low scored, or on the other side too difficult to find the parts, you can leave the expèriment card in any lab, so other players can play with it, or maybe you can play with it in a later visit. f1283bc465bc59648625e6db6744eadef7312970 1013 1012 2013-12-09T13:13:29Z Adela82 4198 wikitext text/x-wiki In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you need to make your monsters. At the beginning, every player starts with two experiments. Every experiment shows a monster made with a colored body (blue, red, pink or gren), eyes (1, 2 or 3) and a mouth (happy or sad) and a lab where you must make it. The parts are spread out over the six labs, so you move to them, and grab the parts you need to make your monsters, but you cannot have more than 5 cards in your hand. Once you have your experiment card and the parts, move to the lab indicated by the experiment card and leave all there (the experiment card and the parts). When other player come to visit that lab, it'll find your monster and then, it will be yours. While you're moving through the labs, you're leaving trails in order to know which player has been the last visiting it. If you move to a lab with a trail, there may be a monster of the owner of the trail. In addition, you can't move to a lab you were the last visit, so you need to program your moves to have all the parts of the experiment before you arrive to the lab where you must leave it with all the parts in your hand. In any time, you may want to go to the center to pick up a new experiment and make new monsters. But you'll not be able to go to the center if you just came from there (your meeple shows laid down) and if you have the maximum cards in hand (five). When a player has 4 monsters done, the center is open to close the game. Any player who goes there, finishes the game. Every monster has an score on it, depending on how difficult is to find their parts. You can see how many parts are in the game of each type, and what are all the experiments in the guide card placed on the bottom left corner of the playboard. The player with the most score points win the game. Advices: If you have experiments, one side too low scored, or on the other side too difficult to find the parts, you can leave the expèriment card in any lab, so other players can play with it, or maybe you can play with it in a later visit. 1665b014e042e0a25ddc91de3c09a9b522506855 1019 1013 2013-12-11T22:18:42Z Trociu 3428 wikitext text/x-wiki ==Cel gry== Zdobyć jak najwięcej punktów za stworzone potwory ==Tworzenie potwora== Każdy potwór składa się z: - oczu: może mieć jedno, dwoje lub troje oczu - buzi: może mieć uśmiech albo grymas - ciała: może być czerwone, zielone, różowe bądź niebieskie Każdy potwór tworzony jest też w jednym, konkretnym laboratorium (z 6 dostępnych). Każda część potwora występują w określenie ilości i widać to na ściądze. Liczba punktów to cyfra w gwiazdce - zarazem oznacza jak trudno jest takiego potwora złożyć (tzn. na im mniej pospolitych fragmentach bazuje). ==Zasady gry== Gracze na zmiany wykonują swoje tury. W czasie swojej tury gracz musi przemieścić się do sąsiedniego laboratorium (z wyjątkiem tych w których już był - określa to żeton w kolorze gracza) lub do środkowej talii talii. Środkowa talia to talia eksperymentów, więc wejście tam wiąże się zawsze z pociągnięciem dodatkowej karty eksperymentu. Jeżeli gracz ma już 5 kart na ręce to nie może tam wejsc (jest to limit kart na ręce). Jeżeli nie może też pójść do innego laboratorium zmuszony jest pozostawić jedną kartę w laboratorium, w którym się znajduje i tym samym traci kolejkę. Gracz, który wchodzi do laboratorium może między nim a swoją ręką przenosić dowolną ilość kart pamiętając o limicie kart na ręce (5 kart). Mogą to być zarówno karty eksperymentów jak i części potwora. Aby stworzyć potwora gracz musi w odpowiednim laboratorium zostawić zarówno części potwora jak i kartę eksperymentu. Potwór jest stworzony dopiero w momencie kiedy inny gracz odwiedzi to laboratorium (po to m.in są znaczniki śladu - by zaznaczyć kto był ostatnio i być może stworzył potwora). Po stworzeniu potwora kartę eksperymentu przekazuje się twórcy (osobie, która ją tam umieściła) i stanowi ona jego punkty. CZĘŚCI POTWORA ZOSTAJĄ W LABORATORIUM I MOŻNA JE WYKORZYSTAĆ NAWET W TEJ SAMEJ TURZE DO STWORZENIA POTWORA! ==Koniec gry== Jeżeli ktoś stworzy 4 potwory (lub więcej - zależne od ustawień stołu) to od tego momentu gra się kończy po turze gracza, który wszedł na środkowy stos. Punkty to cyfry w gwiazdkach i na ich podstawie określa się zwycięzcę. fb8575a9882d5d5fac24591bc1c4a5659306159c 1020 1019 2013-12-12T19:25:32Z Trociu 3428 wikitext text/x-wiki In The Jelly Monster Lab you are an scientist trying to make the best jelly monsters. In order to do it so, you need to move through the labs, trying to find the parts you need to make your monsters. At the beginning, every player starts with two experiments. Every experiment shows a monster made with a colored body (blue, red, pink or gren), eyes (1, 2 or 3) and a mouth (happy or sad) and a lab where you must make it. The parts are spread out over the six labs, so you move to them, and grab the parts you need to make your monsters, but you cannot have more than 5 cards in your hand. Once you have your experiment card and the parts, move to the lab indicated by the experiment card and leave all there (the experiment card and the parts). When other player come to visit that lab, it'll find your monster and then, it will be yours. While you're moving through the labs, you're leaving trails in order to know which player has been the last visiting it. If you move to a lab with a trail, there may be a monster of the owner of the trail. In addition, you can't move to a lab you were the last visit, so you need to program your moves to have all the parts of the experiment before you arrive to the lab where you must leave it with all the parts in your hand. In any time, you may want to go to the center to pick up a new experiment and make new monsters. But you'll not be able to go to the center if you just came from there (your meeple shows laid down) and if you have the maximum cards in hand (five). When a player has 4 monsters done, the center is open to close the game. Any player who goes there, finishes the game. Every monster has an score on it, depending on how difficult is to find their parts. You can see how many parts are in the game of each type, and what are all the experiments in the guide card placed on the bottom left corner of the playboard. The player with the most score points win the game. Advices: If you have experiments, one side too low scored, or on the other side too difficult to find the parts, you can leave the expèriment card in any lab, so other players can play with it, or maybe you can play with it in a later visit. 1665b014e042e0a25ddc91de3c09a9b522506855 Gamehelpraceforthegalaxy 0 41 1015 843 2013-12-11T06:01:20Z Sooz 4387 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one 38dad590c7c548f8bc06cb4fe31ff392cc69d862 1029 1015 2014-01-06T07:51:31Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good b9a76b03a12de68b52a18216d1315508178cd908 1030 1029 2014-01-06T07:52:19Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 82a71cf988b934c0e3883288700b4fae9c846cb1 1031 1030 2014-01-06T07:52:29Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 70967f57bb4d0ff1597fccbd5e6877e460d9bb48 1032 1031 2014-01-06T08:01:38Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 1f7c743a1f6a43edfe085a83425cb51510c5e563 1033 1032 2014-01-06T08:17:40Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 5bdb5660524e4b8f45c515073c8005e7172effdd 1034 1033 2014-01-06T08:17:56Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from /nyour hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 733712790d9a029ae02283aa7855f2904fc26c0d 1035 1034 2014-01-06T08:18:21Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 5bdb5660524e4b8f45c515073c8005e7172effdd 1036 1035 2014-01-06T08:39:16Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech Green = Genes Brown = Rare Elements Blue = Novelty Goods '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good 1f2c804430fd2d2c4d65bc03523df7b5c9742cac 1037 1036 2014-01-06T08:39:50Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech (5) Green = Genes (4) Brown = Rare Elements (3) Blue = Novelty Goods (2) '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a card from your hand to draw new cards (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good acb372225d62f3f811ece974356cadefdbcf1dc3 1038 1037 2014-01-06T08:40:34Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech (5) Green = Genes (4) Brown = Rare Elements (3) Blue = Novelty Goods (2) '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a resource from your hand to draw cards equal to its trade value (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good ca32e154d43da007610ffa53f4b079f5154805e4 1039 1038 2014-01-06T08:42:03Z Drchinchilla 4623 wikitext text/x-wiki In Race for the Galaxy players are competing civilizations exploring space, discovering planets to conquer or settle, building an economy by consuming a planet's goods and building developments. The object of the game is to earn the most victory points from planets and developments played in their tableau, by consuming goods and from bonuses from special developments. There are five phases to choose from: explore, develop, settle, consume and produce. Players pick one phase per turn (two phases in a two-player game). Then everyone plays only the chosen phases in phase order. Players that chose each phase get an extra benefit. At the end of each round players with more than 10 cards discard down to 10 cards and a new round begins. The game continues until one player played 12 cards on their tableau or when the VP pool is exhausted. At the end of that round points are added up. The player with the most points is the winner! In case of a tie, the player with the most goods on the board plus cards in hand wins. You may drag a resource (single-coloured small card) to a consumption power to consume it. '''Summary of Goods''' Yellow = Alien Tech (trade value = 5) Green = Genes (4) Brown = Rare Elements (3) Blue = Novelty Goods (2) '''Summary of Phases''' Explore = draw 2 cards; keep 1, discard the other Develop = place and pay for a development Settle = place and pay for a world, receiving any windfall as indicated Consume = use consume powers if possible, typically to discard goods for victory point chips Produce = place a good on each world without a good that produces one '''Phase Bonuses''' These are the bonuses you gain for choosing each phase: Explore = draw 5 additional cards OR draw 1 additional card and keep 1 additional card Develop = discard 1 less card when paying for the development Settle = draw 1 card after settling a world Consume = Gain double victory points for consumed goods OR trade by discarding a resource from your hand to draw cards equal to its trade value (bonuses from $ row apply) Produce = Produce a good on one windfall world that does not already have a good d7a783556bd5db9c8aebfe061b5b77ac0f4570b0 Contact us 0 9 1021 996 2013-12-18T21:14:23Z Een 3 Reverted edits by [[Special:Contributions/Januszk0|Januszk0]] ([[User talk:Januszk0|talk]]) to last revision by [[User:Sourisdudesert|Sourisdudesert]] wikitext text/x-wiki '''Board Game Arena''' is located in France. == Contact e-mail == contact(at)boardgamearena.com We receive '''a lot''' of e-mails. Please do not send us an e-mail in any of these two cases: * If you want to report a bug, please do it in the [http://boardgamearena.com/#!bugs bug reporting system] * If you want to report a player for violation of BGA policy, use the "report this player" button on his/her profile. == Postal address == G. Isabelli 19 bd république 92 260 Fontenay-aux-Roses FRANCE +33 6 17 25 80 34 980a98bd2ca063ac351422c896d84cf8920c62a0 Gamehelptakenoko 0 104 1022 697 2013-12-22T11:40:22Z Samc1078 4475 Re-wording of the Wind Action. wikitext text/x-wiki Quick Reference Guide Each turn consists of 2 steps: 1) Determine weather 2) Perform 2 different actions and complete objectives Weather Types (always optional) Sun - take a 3rd action Rain - grow 1 bamboo on any irrigated plot Wind - "may" take two identical actions this turn Storm - move panda to any plot and he eats a bamboo as normal Clouds - take an improvement chip from the bank, if no chips are available, choose 1 of the other 4 types of weather for this turn Improvements can only be placed on plots with no bamboo. There can only be 1 improvement on a plot and once placed cannot be changed. Irrigation channels must be between two plots. When a plot is irrigated for the first time it grows bamboo. Plots can only be placed adjacent to the pond or where it is next to 2 other plots in play. The gardener grows bamboo on his plot and all adjacent irrigated plots of the same color. Game ends when a certain number of objectives have been completed. 2 players - 9 objectives 3 players - 8 objectives 4 players - 7 objectives The first player to complete the required number of objectives triggers the final round and scores 2 bonus points. The other players have a final turn. The highest score wins, the tie breaker is number of points on panda objectives. If the score is still tied then all tied players win. 2cd92079359df73e8679ce2711f60b62d4f58bfe Gamehelpacquire 0 142 1023 2013-12-23T19:08:08Z Scooby rex 4486 Created page with "The main object of Acquire is to become the wealthiest player by the end of the game. This is done by forming hotel chains, shrewdly buying the right stock at the right time,..." wikitext text/x-wiki The main object of Acquire is to become the wealthiest player by the end of the game. This is done by forming hotel chains, shrewdly buying the right stock at the right time, merging chains to obtain capital and adding hotels to the chains in which you have controlling interest to increase their value. At the beginning of the game, each player receives $6,000. To determine who plays first, each player draws one tile (representing a hotel) and places it in its matching square on the board. (No chains are formed at this time.) The player who has drawn the lowest number-letter combination starts play. Each player draws six hotels from the central cluster of face-down tiles on the table and keeps them in his/her "hand", not allowing other players to see them. Each player’s turn consists of: 1. Placing one of his hotel tiles on its matching square on the board. If this hotel creates a chain or causes a merger, all transactions connected with Creating Chains or Merging Chains are completed before the player’s turn continues. 2. Buying Stock. If, after placing his hotel, there are one or more hotel chains on the board, the player may, if he wishes, buy stock in one or more of them. (See Buying Stock). 3. Drawing another hotel tile to replace the one played. CREATING NEW CHAINS When a player places a hotel tile next to one already on the board (rank or file—not diagonally), a chain is created. For example, hotel tiles A1 and B2 are on the board but do not form a chain as they adjoin diagonally (not rank or file). Assume that among his six hotels, a player has C2 and B1. He places C2, creating a two-hotel chain. (If he had placed B1, he would have created a three-hotel chain; see above diagram.) Then assume that, on his next turn, he places B1, expanding the chain to four hotels. The maker of a chain chooses its name from seven possible chains, taking the appropriate chain marker and placing it atop any one hotel in the chain. He then receives a "Founder’s Bonus" of one free stock in that chain. A maximum of seven chains may be on the board at one time. Any hotel which would create an eighth chain may not be placed. MERGING CHAINS When a player places a hotel adjacent to two (or more) chains, a merger takes place. The chain with more member hotels takes over the other(s). If the chains are the same size before the merging hotel was placed, the person who made the merger chooses which chain takes over. The chain marker of the now defunct chain is removed. (See below, at Multiple Mergers, for what to do when placing one hotel that merges more than one chain). A chain containing 11 or more hotels is "safe" and cannot be taken over by another chain. A player may not place a hotel which would merge two safe chains. A safe chain may still take over an open chain, however. MAJORITY HOLDER'S BONUS. At merger time, bonuses are paid to the two largest shareholders in the defunct chain. In the case of a tie for the title of largest shareholder, the first and second bonuses are combined and divided equally between the tying shareholders. In the case of a tie for the title of second largest shareholder, the second bonus is divided equally between the tying players. If only one player holds stock in the defunct chain, he receives both bonuses. DISPOSAL OF STOCK AT TIME OF MERGER. After the bonuses have been paid, each player, starting with the merger maker and continuing clockwise, handles his/her stock in the defunct chain in one or more of the following ways: 1. Hold: Stock may be held in expectation of starting another chain with that name. 2. Sell: Stock may be sold to Stock Market at a price determined by the number of hotels in the defunct chain before the merger. 3. Trade: Stock may be traded at the rate of two blocks of defunct stock for one block of controlling chain stock. (If the Stock Market has no remaining blocks of controlling chain stock, players may not trade). MULTIPLE MERGERS. When multiple mergers (mergers of more than two chains at once) occur, the larger chain takes over all smaller chains simultaneously. The multiple merger is then handled as individual mergers. Majority holder’s bonuses are paid for the larger of defunct chains and players dispose of their stock in this chain. Then the smaller defunct chain is handled in the same manner. In the case of a tie, the merger maker chooses which defunct chain is to be handled first. Bonuses and stock prices are determined by the number of hotels in the defunct chain before the merger. BUYING STOCK Any player, immediately after playing his hotel, may buy stock in any active chain—up to a maximum of three blocks in any one turn. If he is unable to place any of his hotels, he may still buy stock. His purchase may be in one, two or three different chains. Price per block varies with the number of hotels in the chain. A player who runs out of money cannot buy stock but must place a hotel if he is able. He cannot raise money by selling stock except during the designated disposal period after a merger. Trading and selling of stock between players is not permitted. At any time, player may ask how much stock remains in a particular chain. ENDING THE GAME The game ends when one player, during his turn, announces that either all chains on board are "safe" or that any one chain contains 41 or more hotels. A player does not have to announce that the game is over if it is to his advantage to continue playing. After announcing that the game is over, the player may still complete his turn. To determine the winner, all chains are bought out by the bank. Majority holder’s bonuses are paid for all active chains. All players sell their stock. Stock in a chain that is not on the board is worthless. The player with the most money is the winner. 6daccd417644006bb74530f1659ad57b06feb515 Gamehelpsaboteur 0 32 1024 85 2014-01-03T13:59:28Z Tomcio8802 4596 /* Available variants */ wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other saboteurs, prevent the gold diggers from getting to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) '''Have a good game !''' The Official Anthem of Saboteur designed by '''Dzikidon''' Music - '''Adele - Skyfall''': *This is the end *Hold your breath and count to ten *Feel the Earth move and then *Hear your axe burst again *For this is the end *I’ve digged and dreamt this moment *I will repair I owe them *Even tho' I’m broken *Let the rock fall *When it crumbles *We will stand tall *Face it all together *Let the rock fall *When it crumbles *We will stand tall *Face it all together *At rock fall *At rock fall *Rock fall is where we start *A thousand miles and cards apart *Where roads collide and caves are dark *You may fix my trolley *You can break my lamp *But you’ll never have my gold *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *At rock fall *(Let the rock fall) *When it crumbles *We will stand tall) / x2 *Where you go I go *What you see I see *I know I'd never be me *Without the security *Of your fixing cards *Keeping me from harm *Put your end in their straight *And we'll stand *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *At rock fall *Let the rock fall *We will stand tall *At rock faaall a26d5ddfd4f3a8f6cb5a00a894689eba87be1aeb 1025 1024 2014-01-03T15:40:21Z Dzikidon 4598 wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other saboteurs, prevent the gold diggers from getting to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) '''Have a good game !''' The Official Anthem of Saboteur designed by '''Dzikidon''' and '''Sikora8a''' Music - '''Adele - Skyfall''': *This is the end *Hold your breath and count to ten *Feel the Earth move and then *Hear your axe burst again *For this is the end *I’ve digged and dreamt this moment *I will repair I owe them *Even tho' I’m broken *Let the rock fall *When it crumbles *We will stand tall *Face it all together *Let the rock fall *When it crumbles *We will stand tall *Face it all together *At rock fall *At rock fall *Rock fall is where we start *A thousand miles and cards apart *Where roads collide and caves are dark *You may fix my trolley *You can break my lamp *But you’ll never have my gold *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *At rock fall *(Let the rock fall) *When it crumbles *We will stand tall) / x2 *Where you go I go *What you see I see *I know I'd never be me *Without the security *Of your fixing cards *Keeping me from harm *Put your end in their straight *And we'll stand *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *Let the rock fall (Let the rock fall) *When it crumbles (When it crumbles) *We will stand tall (We will stand tall) *Face it all together *At rock fall *Let the rock fall *We will stand tall *At rock faaall 30b04f63dd36cb299d123cf23b3b25817fc5692a Faq 0 3 1026 968 2014-01-04T14:21:38Z Scooby rex 4486 /* What is forbidden on Board Game Arena? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[Browser_support|browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Taking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple account and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. ea83ef3f991f3e664f77c8065e4eb3e9fc45eaf1 1027 1026 2014-01-04T14:22:20Z Scooby rex 4486 /* What is absolutely forbidden in Board Game Arena? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[Browser_support|browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Taking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple accounts and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== Board Game Arena platform has been designed to encourage players to maintain a good behaviour. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had a bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 642a6f93e07228e0a55c19a3084a2d07d8f425f3 1028 1027 2014-01-04T14:23:36Z Scooby rex 4486 /* What if some player does something wrong? */ wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[Browser_support|browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Taking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple accounts and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== The Board Game Arena platform has been designed to encourage players to maintain good behavior. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. 6d2f68407a8ecc16b22184f0a4513f30c364e426 Main game logic: yourgamename.game.php 0 86 1040 946 2014-01-06T08:45:43Z Sourisdudesert 1 wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your gameinfos.inc.php file like this: <pre> 'tie_breaker_description' => totranslate("Describe here your tie breaker formula"), </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaVisibleSystemException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaVisibleSystemException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. == Player elimination == In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end. This case should be rare. Please don't use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature is e8c03a720c7bf9dd99f27da4b138df14a86868a0 1041 1040 2014-01-06T08:53:15Z Sourisdudesert 1 /* Player elimination */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states. * Game state actions: the logic to run when entering a new game state. * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your gameinfos.inc.php file like this: <pre> 'tie_breaker_description' => totranslate("Describe here your tie breaker formula"), </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaVisibleSystemException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaVisibleSystemException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. == Player elimination == In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end. This case should be rare. Please don't use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature should be used only in games where players are eliminated all along the game (typical examples: "Perudo" or "The Werewolves of Miller's Hollow"). Usage: * Player to eliminate should NOT be active anymore (preferably use the feature in a "game" type game state). * In your PHP code: self::eliminatePlayer( <player_to_eliminate_id> ); * the player is informed in a dialog box that he no longer have to played and can start another game if he/she wants too (whith buttons "stay at this table" "quit table and back to main site"). In any case, the player is free to tart&join another table from now. * When your game is over, all players who have been eliminated before receive a "notification" (the small "!" icon on the top right of the BGA interface) that indicate them that "the game has ended" and invite them to review the game results. 62154d567a823d8cb941350f88708e148cd49684 1042 1041 2014-01-06T09:00:34Z Sourisdudesert 1 /* File Structure */ wikitext text/x-wiki This file is the main file for your game logic. Here you initialize the game, persist data, implement the rules and notify changes to the client interface. == File Structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * EmptyGame (constructor): where you define global variables. * setupNewGame: initial setup of the game. * getAllDatas: where you retrieve all game data during a complete reload of the game. * getGameProgression: where you compute the game progression indicator. * Utility functions: your utility functions. * Player actions: the entry points for players actions. * Game state arguments: methods to return additional data on specific game states ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#args more info here]). * Game state actions: the logic to run when entering a new game state ([http://en.doc.boardgamearena.com/Your_game_state_machine:_states.inc.php#action more info here]). * zombieTurn: what to do it's the turn of a zombie player. == Accessing player informations == ; getPlayersNumber() : Returns the number of players playing at the table : Note: doesn't work in setupNewGame so use count($players) instead ; getActivePlayerId() : Get the "active_player", whatever what is the current state type. : Note: it does NOT mean that this player is active right now, because state type could be "game" or "multiplayer" : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; getActivePlayerName() : Get the "active_player" name : Note: avoid using this method in a "multiplayer" state because it does not mean anything. ; loadPlayersBasicInfos() : Get an associative array with generic data about players (ie: not game specific data). : The key of the associative array is the player id. : The content of each value is: : * player_name : * player_color (ex: ff0000) ; getCurrentPlayerId() : Get the "current_player". The current player is the one from which the action originated (the one who send the request). : '''Be careful''': It is not always the active player. : In general, you shouldn't use this method, unless you are in "multiplayer" state. ; getCurrentPlayerName() : Get the "current_player" name : Be careful using this method (see above). ; getCurrentPlayerColor() : Get the "current_player" color : Be careful using this method (see above). ; isCurrentPlayerZombie() : Check the "current_player" zombie status. If true, player leave the game. == Accessing database == The main game logic should be the only point from where you should access to the game database. You access your database using SQL queries with the methods below. '''IMPORTANT''' BGA is using [http://dev.mysql.com/doc/refman/5.0/en/sql-syntax-transactions.html database transactions]. It means that your database changes WON'T BE APPLIED to the database until your request ends normally. Using transaction is in fact very useful for you: at any time, if your game logic detects that something is wrong (ex: unallowed move), you just have to throw an exception and all the changes already performed on the game situation will be removed. ; DbQuery( $sql ) : This is the generic method to access the database. : It can execute any type of SELECT/UPDATE/DELETE/REPLACE query on the database. : You should use it for UPDATE/DELETE/REPLACE query. For SELECT queries, the specialized methods above are much better. ; getUniqueValueFromDB( $sql ) : Returns a unique value from DB or null if no value is found. : $sql must be a SELECT query. : Raise an exception if more than 1 row is returned. ; getCollectionFromDB( $sql, $bSingleValue=false ) : Returns an associative array of rows for a sql SELECT query. : The key of the resulting associative array is the first field specified in the SELECT query. : The value of the resulting associative array if an associative array with all the field specified in the SELECT query and associated values. : First column must be a primary or alternate key. : The resulting collection can be empty. : If you specified $bSingleValue=true and if your SQL query request 2 fields A and B, the method returns an associative array "A=>B" Example 1: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( 1234 => array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), 1235 => array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getCollectionFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 1234 => 'myuser0', 1235 => 'myuser1' ) </pre> ; getNonEmptyCollectionFromDB( $sql ) : Idem than previous one, but raise an exception if the collection is empty ; function getObjectFromDB( $sql ) : Returns one row for the sql SELECT query as an associative array or null if there is no result : Raise an exception if the query return more than one row Example: <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_score score FROM player WHERE player_id='$player_id'" ); Result: array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ) </pre> ; getNonEmptyObjectFromDB( $sql ) : Idem than previous one, but raise an exception if no row is found ; getObjectListFromDB( $sql, $bUniqueValue=false ) : Return an array of rows for a sql SELECT query. : the result if the same than "getCollectionFromDB" except that the result is a simple array (and not an associative array). : The result can be empty. : If you specified $bUniqueValue=true and if your SQL query request 1 field, the method returns directly an array of values. Example 1: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name, player_score score FROM player" ); Result: array( array( 'id'=>1234, 'name'=>'myuser0', 'score'=>1 ), array( 'id'=>1235, 'name'=>'myuser1', 'score'=>0 ) ) </pre> Example 2: <pre> self::getObjectListFromDB( "SELECT player_id id, player_name name FROM player", true ); Result: array( 'myuser0', 'myuser1' ) </pre> ; getDoubleKeyCollectionFromDB( $sql, $bSingleValue=false ) : Return an associative array of associative array, from a SQL SELECT query. : First array level correspond to first column specified in SQL query. : Second array level correspond to second column specified in SQL query. : If bSingleValue = true, keep only third column on result ; DbGetLastId() : Return the PRIMARY key of the last inserted row (see PHP mysql_insert_id function). ; DbAffectedRow() : Return the number of row affected by the last operation ; escapeStringForDB( $string ) : You must use this function on every string type data in your database that contains unsafe data. : (unsafe = can be modified by a player). : This method makes sure that no SQL injection will be done through the string used. <pre> self::getObjectFromDB( "SELECT player_id id, player_name name, player_color color FROM player WHERE player_id='1234'" ); Result: array( 'id' => 1234, 'name' => 'myuser1', 'color' => 'ff0000' ) </pre> ; function getNonEmptyObjectFromDB( $sql ) : Idem, but raise an exception if the query doesn't return exactly one row Note: see Editing [[Game database model: dbmodel.sql]] to know how to define your database model. == Use globals == Sometimes, you have to keep a single integer value that is global to your game, and you don't want to create a DB table specifically for it. Using a BGA framework "global", you can do such a thing. Your value will be stored in the "global" table in database, and you can access it with simple methods. '''initGameStateLabels''' This method is located at the beginning of your game logic. This is the place you defines the globals used in your game logic, by assigning them IDs. You can define up to 89 globals, with IDs from 10 to 89. You must NOT use globals outside this range as globals are used by other components of the framework. <pre> self::initGameStateLabels( array( "my_first_global_variable" => 10, "my_second_global_variable" => 11 ) ); </pre> '''setGameStateInitialValue( $value_label, $value_value )''' Init your global value. Must be called before any use of your global, so you should call this method from your "setupNewGame" method. '''getGameStateValue( $value_label )''' Retrieve the current value of a global. '''setGameStateValue( $value_label, $value_value )''' Set the current value of a global. '''incGameStateValue( $value_label, $increment )''' Increment the current value of a global. If increment is negative, decrement the value of the global. Return the final value of the global. == Game states and active players == ; checkAction( $actionName, $bThrowException=true ) : Check if action is valid regarding current game state (exception if fails) : The action is valid if it is listed as a "possibleactions" in the current game state (see game state description). : This method MUST be called in the first place in ALL your PHP methods that handle players action, in order to make sure a player can't do an action when the rules disallow it at this moment of the game. : if "bThrowException" is set to "false", the function return false in case of failure instead of throwing and exception. This is useful when several actions are possible in order to test each of them without throwing exceptions. ; activeNextPlayer() : Make the next player active in the natural player order. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; activePrevPlayer() : Make the previous player active (in the natural player order). : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->changeActivePlayer( $player_id ) : You can call this method to make any player active. : Note: you CANT use this method in a "activeplayer" or "multipleactiveplayer" state. You must use a "game" type game state for this. ; $this->gamestate->getActivePlayerList() : With this method you can retrieve the list of the active player at any time. : During a "game" type gamestate, it will return a void array. : During a "activeplayer" type gamestate, it will return an array with one value (the active player id). : during a "multipleactiveplayer" type gamestate, it will return an array of the active players id. : Note: you should only use this method is the latter case. ; $this->gamestate->setAllPlayersMultiactive() : With this method, all playing players are made active. : Usually, you use this method at the beginning (ex: "st" action method) of a multiplayer game state when all players have to do some action. ; $this->gamestate->setPlayersMultiactive( $players, $next_state ) : Make a specific list of players active during a multiactive gamestate. : Bare in mind it doesn't deactivate other previously active players. : "players" is the array of player id that should be made active. : In case "players" is empty, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->setPlayerNonMultiactive( $player_id, $next_state ) : During a multiactive game state, make the specified player inactive. : Usually, you call this method during a multiactive game state after a player did his action. : If this player was the last active player, the method trigger the "next_state" transition to go to the next game state. ; $this->gamestate->checkPossibleAction( $action ) : (rarely used) : This works exactly like "checkAction", except that it do NOT check if current player is active. : This is used specifically in certain game states when you want to authorize some additional actions for players that are not active at the moment. : Example: in Libertalia game, you want to authorize players to change their mind about card played. They are of course not active at the time they change their mind, so you cannot use "checkAction" and use "checkPossibleAction" instead. == Players turn order == '''getNextPlayerTable()''' Return an associative array which associate each player with the next player around the table. In addition, key 0 is associated to the first player to play. Example: if three player with ID 1, 2 and 3 are around the table, in this order, the method returns: <pre> array( 1 => 2, 2 => 3, 3 => 1, 0 => 1 ); </pre> '''getPrevPlayerTable()''' Same as above, but the associative array associate the previous player around the table. '''getPlayerAfter( $player_id )''' Get player playing after given player in natural playing order. '''getPlayerBefore( $player_id )''' Get player playing before given player in natural playing order. == Notify players == To understand notifications, please read [http://www.slideshare.net/boardgamearena/the-bga-framework-at-a-glance The BGA Framework at a glance] first. '''IMPORTANT''' Notifications are sent at the very end of the request, when it ends normally. It means that if you throw an exception for any reason (ex: move not allowed), no notifications will be sent to players. '''notifyAllPlayers( $notification_type, $notification_log, $notification_args )''' Send a notification to all players of the game. * notification_type: A string that defines the type of your notification. Your game interface Javascript logic will use this to know what is the type of the received notification (and to trigger the corresponding method). * notification_log: A string that defines what is to be displayed in the game log. You can use an empty string here (""). In this case, nothing is displayed in the game log. If you define a real string here, you should use "clienttranslate" method to make sure it can be translate. You can use arguments in your notification_log strings, that refers to values defines in the "notification_args" argument (see below). Note: you CAN use some HTML inside your notification log, and it is working. However: _ pay attention to keep the log clear. _ try to not include some HTML tags inside the "clienttranslate" method, otherwise it will make the translators work more difficult. You can use a notification argument instead, and provide your HTML through this argument. * notification_args: The arguments of your notifications, as an associative array. This array will be transmitted to the game interface logic, in order the game interface can be updated. Complete notifyAllPlayers example (from "Reversi"): <pre> self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); </pre> You can see in the example above the use of the "clienttranslate" method, and the use of 2 arguments "player_name" and "returned_nbr" in the notification log. Important: NO private date must be sent with this method, as a cheater could see it even it is not used explicitly by the game interface logic. If you want to send private information to a player, please use notifyPlayer below. '''notifyPlayer( $player_id, $notification_type, $notification_log, $notification_args )''' Same as above, except that the notification is sent to one player only. This method must be used each time some private information must be transmitted to a player. == Game statistics == There are 2 types of statistics: * a "player" statistic is a statistic associated to a player * a "table" statistics is a statistic not associated to a player (global statistic for this game). See [[Game statistics: stats.inc.php]] to see how you defines statistics for your game. '''initStat( $table_or_player, $name, $value, $player_id=null )''' Create a statistic entry for the specified statistics with a default value. This method must be called for each statistics of your game, in your setupNewGame method. 'table_or_player' must be set to "table" if this is a table statistics, or "player" if this is a player statistics. 'name' is the name of your statistics, as it has been defined in your stats.inc.php file. 'value' is the initial value of the statistics. If this is a player statistics and if the player is not specified by "player_id" argument, the value is set for ALL players. '''function setStat( $value, $name, $player_id = null )''' Set a statistic value. If "player_id" is not specified, setStat consider it is a TABLE statistic. If "player_id" is specified, setStat consider it is a PLAYER statistic. '''incStat( $delta, $name, $player_id = null )''' Increment (or decrement) specified statistic value. Same behavior as above. == Translations == See [[Translations]] == Manage player scores and Tie breaker == At the end of the game, players automatically get a rank depending on their score: the player with the biggest score is #1, the player with the second biggest score is #2, and so on... During the game, you update player's score directly by updating "player_score" field of "player" table in database. Examples: <pre> // +2 points to active player self::DbQuery( "UPDATE player SET player_score=player_score+2 WHERE player_id='".self::getActivePlayerId()."'" ); // Set score of active player to 5 self::DbQuery( "UPDATE player SET player_score=5 WHERE player_id='".self::getActivePlayerId()."'" ); </pre> Note: don't forget to notify the client side in order the score control can be updated accordingly. '''Tie breaker''' Tie breaker is used when two players get the same score at the end of a game. Tie breaker is using "player_score_aux" field of "player" table. It is updated exactly like the "player_score" field. Tie breaker score is displayed only for players who are tied at the end of the game. Most of the time, it is not supposed to be displayed explicitly during the game. When you are using "player_score_aux" functionality, you must describe the formula to use in your gameinfos.inc.php file like this: <pre> 'tie_breaker_description' => totranslate("Describe here your tie breaker formula"), </pre> This description will be used as a tooltip to explain to players how this auxiliary score has been calculated. == Reflexion time == ; function giveExtraTime( $player_id, $specific_time=null ) : Give standard extra time to this player. : Standard extra time depends on the speed of the game (small with "slow" game option, bigger with other options). : You can also specify an exact time to add, in seconds, with the "specified_time" argument (rarely used). == Managing errors and exceptions == Note: when you throw an exception, all database changes and all notifications are cancelled immediately. This way, the game situation that were existing before the request is completely restored. ; throw new BgaUserException ( $error_message) : Base class to notify a user error : You must throw this exception when a player want to do something that he is not allowed to do. : The error message will be shown to the player as a "red message", so it must be translated. : Throwing such an exception is NOT considered as a bug, so it is not traced in BGA error logs. Example from Gomoku: <pre> throw new BgaUserException( self::_("There is already a stone on this intersection, you can't play there") ); </pre> ; throw new BgaVisibleSystemException ( $error_message) : You must throw this exception when you detect something that is not supposed to happened into your code. : The error message is shown to the user as an "Unexpected error", in order he can report it in the forum. : The error message is logged in BGA error logs. If it happens regularly, we will report it to you. ; throw new BgaSystemException ( $error_message) : Base class to notify a system exception. The message will be hidden from the user, but show in the logs. Use this if the message contains technical information. : You shouldn't use this type of exception except if you think the information shown could be critical. Indeed: a generic error message will be shown to the user, so it's going to be difficult for you to see what happened. == Zombie mode == When a player leaves a game for any reason (expelled, quit), he becomes a "zombie player". In this case, the results of the game won't count for statistics, but this is cool if the other players can finish the game anyway. That's why zombie mode exists: allow the other player to finish the game, even if the situation is not ideal. While developing your zombie mode, keep in mind that: * Do not refer to the rules, because this situation is not planned by the rules. * Try to figure that you are playing with your friends and one of them has to leave: how can we finish the game without killing the spirit of the game? * The idea is NOT to develop an artificial intelligence for the game. Most of the time, the best thing to do when it is zombie player turn is to jump immediately to a state where he is not active anymore. For example, if he is in a game state where he has a choice between playing A and playing B, the best thing to do is NOT to choose A or B, but to pass. So, even if there's no "pass" action in the rules, add a "zombiepass" transitition in your game state and use it. Each time a zombie player must play, your "zombieTurn" method is called. Parameters: * $state: the name of the current game state. * $active_player: the id of the active player. Most of the time, your zombieTurn method looks like this: <pre> function zombieTurn( $state, $active_player ) { $statename = $state['name']; if( $statename == 'myFirstGameState' || $statename == 'my2ndGameState' || $statename == 'my3rdGameState' .... ) { $this->gamestate->nextState( "zombiePass" ); } else throw new BgaVisibleSystemException( "Zombie mode not supported at this game state: ".$statename ); } </pre> Note that in the example above, all corresponding game state should implement "zombiePass" as a transition. == Player elimination == In some games, this is useful to eliminate a player from the game in order he/she can start another game without waiting for the current game end. This case should be rare. Please don't use player elimination feature if some player just has to wait the last 10% of the game for game end. This feature should be used only in games where players are eliminated all along the game (typical examples: "Perudo" or "The Werewolves of Miller's Hollow"). Usage: * Player to eliminate should NOT be active anymore (preferably use the feature in a "game" type game state). * In your PHP code: self::eliminatePlayer( <player_to_eliminate_id> ); * the player is informed in a dialog box that he no longer have to played and can start another game if he/she wants too (whith buttons "stay at this table" "quit table and back to main site"). In any case, the player is free to tart&join another table from now. * When your game is over, all players who have been eliminated before receive a "notification" (the small "!" icon on the top right of the BGA interface) that indicate them that "the game has ended" and invite them to review the game results. 145ce8691534997b3b513ef02519689d6e065ee9 Your game state machine: states.inc.php 0 90 1043 804 2014-01-06T09:09:19Z Sourisdudesert 1 /* args */ wikitext text/x-wiki This file describes the game states machine of your game (all the game states properties, and the transitions to get from one state to another). Important: to understand the game state machine, the best is to read this presentation first: [http://www.slideshare.net/boardgamearena/bga-studio-focus-on-bga-game-state-machine Focus on BGA game state machine] == Overall structure == The machine states is described by a PHP associative array. Example: <pre> $machinestates = array( // The initial state. Please do not modify. 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 2 ) ), // Note: ID=2 => your first state 2 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a card or pass'), "descriptionmyturn" => clienttranslate('${you} must play a card or pass'), "type" => "activeplayer", "possibleactions" => array( "playCard", "pass" ), "transitions" => array( "playCard" => 2, "pass" => 2 ) ), </pre> == Syntax == === id === The keys determine game states IDs (in the example above: 1 and 2). IDs must be positive integers. ID=1 is reserved for the first game state and should not be used (and you must not modify it). ID=99 is reserved for the last game state of the game (end of the game) (and you must not modify it). Note: you may use any ID, even ID greater than 100. But you cannot use 1 and 99. Note²: You can't of course use the same ID twice. === name === (mandatory) The name of a game state is used to identify it in your game logic. Several game states can share the same name, however this is not recommended. PHP example: <pre> // Get current game state $state = $this->gamestate->state(); if( $state['name'] == 'myGameState' ) { ... } </pre> JS example: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) case 'myGameState': // Do some stuff at the beginning at this game state .... break; </pre> === type === (mandatory) You can use 3 types of game states: * activeplayer (1 player is active and must play) * multipleactiveplayer (1..N players can be active and must play) * game (no player is active. This is a transitional state to do something automatic specified by game rules) === description === (mandatory) The description is the string that is displayed in the main action bar (top of the screen) when the state is active. When a string is specified as a description, you must use "clienttranslate" in order the string can be translate on the client side: <pre> "description" => clienttranslate('${actplayer} must play a card or pass'), </pre> In the description string, you can use ${actplayer} to refer to the active player. You can also use custom arguments in your description. These custom arguments correspond to values returned by your "args" PHP method (see below "args" field). Example of custom field: <pre> In states.inc.php: "description" => clienttranslate('${actplayer} must choose ${nbr} identical energies'), "args" => "argMyArgumentMethod" In mygame.game.php: function argMyArgumentMethod() { return array( 'nbr' => 2 // In this case ${nbr} in the description will be replaced by "2" ); } </pre> Note: You may specify an empty string ("") here if it never happens that the game remains in this state (ie: if this state immediately jump to another state when activated). Note²: Usually, you specify a string for "activeplayer" and "multipleactiveplayer" game states, and you specify an empty string for "game" game states. BUT, if you are using synchronous notifications, the client can remains few seconds on a "game" type game state, and in this case this may be useful to display a description in the status bar during this state. === descriptionmyturn === (mandatory for "activeplayer" and "multipleactiveplayer" game states type) "descriptionmyturn" has exactly the same role and properties than "description", except that this value is displayed to the current active player - or to all active players in case of a multipleactiveplayer game state. In general, we have this situation: <pre> "description" => clienttranslate('${actplayer} can take some actions'), "descriptionmyturn" => clienttranslate('${you} can take some actions'), </pre> Note: you can use ${you} in description my turn in order the description can display "You" instead of the name of the player. === action === (mandatory for "game" game state type) "action" specify a PHP method to call when entering into this game state. Example: <pre> In states.inc.php: 28 => array( "name" => "startPlayerTurn", "description" => '', "type" => "game", "action" => "stStartPlayerTurn", In mygame.game.php: function stStartPlayerTurn() { // ... do something at the beginning of this game state </pre> Usually, for "game" game state type, the action method is used to do some automatic stuff specified by the rules (ex: check victory conditions, deal cards for a new round, go to the next player...) and then jump to another game state. Note: a BGA convention specify that PHP method called with "action" are prefixed by "st". === transitions === (mandatory) With "transition" you specify in which game state you can jump from a given game state. Example: <pre> 25 => array( "name" => "myGameState", "transitions" => array( "nextPlayer" => 27, "endRound" => 39 ), .... } </pre> In the example above, if "myGameState" is the current active game state, I can jump to game state with ID 27, or game state with ID 39. Example to jump to ID 27: <pre> In mygame.game.php: $this->gamestate->nextState( "nextPlayer" ); </pre> Important: "nextPlayer" is the name of the transition, and NOT the name of the target game state. Several transitions can lead to the same game state. Note: if you have only 1 transition, you may give it an empty name. Example: <pre> In states.inc.php: "transitions" => array( "" => 27 ), In mygame.game.php: $this->gamestate->nextState( ); // We don't need to specify a transition as there is only one here </pre> === possibleactions === (mandatory for "activeplayer" and "multipleactiveplayer" game states) "possibleactions" defines the actions possible by the players at this game state. By defining "possibleactions", you make sure players can't do actions that they are not allowed to do at this game states. Example: <pre> In states.game.php: "possibleactions" => array( "playCard", "pass" ), In mygame.game.php: function playCard( ...) { self::checkAction( "playCard" ); // Will failed if "playCard" is not specified in "possibleactions" in current game state. .... In mygame.js: playCard: function( ... ) { if( this.checkAction( "playCard" ) ) // Will failed if "playCard" is not specified in "possibleactions" in current game state. { return ; } .... </pre> === args === (optional) From time to time, it happens that you need some information on the client side (ie : for your game interface) only for a specific game state. Example 1 : for Reversi, the list of possible moves during playerTurn state. Example 2 : in Caylus, the number of remaining king's favor to choose in the state where the player is choosing a favor. Example 3 : in Can't stop, the list of possible die combination to be displayed to the active player in order he can choose among them. In such a situation, you can specify a method name as the « args » argument for your game state. This method must get some piece of information about the game (ex : for Reversi, the possible moves) and return them. Thus, this data can be transmitted to the clients and used by the clients to display it. It should always be an associative array. Let's see a complete example using args with « Reversi » game : In states.inc.php, we specify some « args » argument for gamestate « playerTurn » : <pre> 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", <================================== HERE "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), </pre> It corresponds to a « argPlayerTurn » method in our PHP code (reversi.game.php): <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves() ); } </pre> Then, when we enter into « playerTurn » game state on the client side, we can highlight the possible moves on the board using information returned by argPlayerTurn : <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> Note: you can also use values returned by your "args" method to have some custom values in your "description"/"descriptionmyturn" (see above). Note: as a BGA convention, PHP methods called with "args" are prefixed by "arg" (ex: argPlayerTurn). ==== Private infos in args ==== By default, all data provided through this method are PUBLIC TO ALL PLAYERS. Please do not send any private data with this method, as a cheater could see it even it is not used explicitly by the game interface logic. This is although possible to specify that some data should be sent to some specific players only: Example 1: send an information to active player(s) only: <pre> function argPlayerTurn() { return array( '_private' => array( // Using "_private" keyword, all data inside this array will be made private 'active' => array( // Using "active" keyword inside "_private", you select active player(s) 'somePrivateData' => self::getSomePrivateData() // will be send only to active player(s) ) ), 'possibleMoves' => self::getPossibleMoves() // will be send to all players ); } </pre> Example 2: send an information to a specific player (<specific_player_id>) only: <pre> function argPlayerTurn() { return array( '_private' => array( // Using "_private" keyword, all data inside this array will be made private <specific_player_id> => array( // you select one specific player with its id 'somePrivateData' => self::getSomePrivateData() // will be send only to <specific_player_id> ) ), 'possibleMoves' => self::getPossibleMoves() // will be send to all players ); } </pre> IMPORTANT: in certain situation (ex: multipleactiveplayer game state) these "private data" features can have a big performance impact. Please do not use if not needed. === updateGameProgression === (optional) IF you specify "updateGameProgression => true" in a game state, your "getGameProgression" PHP method will be called at the beginning of this game state - and thus the game progression of the game will be updated. At least one of your game state (any of them) must specify updateGameProgression=>true. 662e0aefe4d493cd3263698181e113351d75c6a2 Moderationpolicy 0 123 1044 867 2014-01-06T12:17:50Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games played. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: explicit sexual content, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (explicit sexual content, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one noticed the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it has to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong intent to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. It is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP address, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Translation stealing / vandalism | The player is writing inappropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their allotted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} fd3b875814a1865e5ca8f4def483c31d94b46a3b 1045 1044 2014-01-06T12:23:54Z Sourisdudesert 1 /* Penalty grid */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games played. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: explicit sexual content, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (explicit sexual content, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one noticed the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it has to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong intent to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. It is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP address, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Spam / advertising / inappropriate links | The player is using BGA for advertising. In particular: links not related to the purpose of the page, advertising for other online boardgaming websites, spam, ... | |- ! Something else | Translation stealing / vandalism | The player is writing inappropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their allotted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 34ec666062af04c36bf1fccc6cc61e8f415eb55b 1046 1045 2014-01-06T13:24:42Z Sourisdudesert 1 /* About private jokes */ wikitext text/x-wiki == General rules == === Beyond a Reasonable Doubt === In case there is a any possible doubt, a player should be considered not guilty. === Moderators independance === Moderators must not process reports if they know the protagonists (author or targeted player). Playing online with the protagonist does not mean to "know" them. "Know" means: know them personally or playing with them online very regularly. === Appeal === Players has the right to appeal against the moderation decision, by forwarding received moderation email to "contact@boardgamearena.com". They '''MUST''' explain in the email why the moderator did not respect the BGA moderation policy. If the explanation is not convincing, the email will be ignored. If the player is obviously guilty and if the moderation policy hasn't been violated, BGA admins will double the penalties. === Penalty grid === Moderators should respect the penalties grid as much as possible. However, they have the right to adjust the penalty depending on the gravity of the case. The following situation can lead moderators to increase the penalties: * the player has been reported several times for the same type of case. * the player wrong action can be obviously qualified by one or several of the following terms: racism, xenophobia, homophobia, sexism, and as a rule of thumb any type of discrimination against a particular category of people. * on case there is a provocation, the penalty may be be reduced a little, and the author of the provocation may have a warning BUT, as everyone should keep quiet and polite even if there are provoked, this is a moderator choice. === Relapse === In case there is a relapse, the penalty increases exponentially. This is the moderator job to determine the right penalty to apply in this case, considering the fact that the player has been warned by a first penalty. Moderators should consider there is a relapse only if the new wrong action occurs AFTER the previous penalty has been applied and the associated moderation email has been sent. Considering it's a lot of work to analyze reports about players who do the same action again and again, the "ban forever" penalty can be applied to any case if the player relapses. === Special urgency ban procedure === Whatever the case type, moderators has the right to ban a player from the website (forever) if this player set up a situation that is out of control and must be stopped within hours. This special procedure should only be used if there is an immediate danger for the community (ex: vandalism, flow of insults, massive spam, ...). === About private jokes === If a wrong action happened in a private circle (ex: at a game table, with no spectators), and if none of the present players reported the action, the moderator must consider this was a "private joke" and must not apply any penalty. In the contrary, if the wrong action is public (ex: post/comment on the wall), the moderator may consider this as a wrong action even if there is no report. Note: in case a moderator discovers that this is obviously not a private joke and that something must be reported, the moderator is allowed to create a report for the case. However, in this case, this moderator is not allowed to judge the report. === Prescription === For operational reason, a report that is older than 2 weeks may be closed by moderators because it is too old. === Player profile influence === The player profile IS taken into account to determine if the player is guilty or not guilty. Moderators give more credit to players with a good profile than the other. The player profile is NOT taken into account when choosing the penalty if the player is found guilty. Good profile elements are: * A good thumb up / thumb down ratio. * Player is registered since a long time / has a lot of games played. * Players is involved in the website (ex: realized translations). * Player is supporting BGA (member of the club). === Incomplete reports === Analyzing reports takes a lot of time, and many reports are received. If any needed information is missing in the report, moderators may (and most of the time, should) close the report. == Penalty grid == {| class="wikitable alternance centre" |+ Titre |- ! scope="col" | Category ! scope="col" | Case type ! scope="col" | Details ! scope="col" | Standard penalty |- ! Insults | Insults during a game, on table chat | Must be present in the report: table ID, and the insults terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on general chat | Must be present in the report: the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 30 days for small insults and insanity, Devilkin forever for real insults. In any case: the player should be warn that this is the last warning before ban. |- ! Insults | Insults on public space (wall, forum, comments...) | Must be present in the report: where to find the insult, the insult terms. If the insult is not in English, or if the insult is not obvious, the report author must specify why this is insulting. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Aggressive attitude | Aggressive attitude during a game: can't wait the other player, provocation, ... | As a rule of thumb: as long as there is no insult, this is not an issue: thumb down are the solution. BUT, if the player is really borderline and should be warned, a moderation action is needed. | -1 to -10 reputation penalty. Moderators should explain that this is a small warning, and that a change of attitude is needed. |- ! Avatar | Inappropriate avatar: explicit sexual content, extreme violence, advertising... | If needed, report author should explain why this is inappropriate. Avatar is really inappropriate in 3 cases: 1°) It can hurt underage players (explicit sexual content, extreme violence) 2°) This is advertisement / this violate some intellectual property 3°) The avatar contains a reference that discriminate a group of people (ex: nazi cross) | Avatar immediate removal + Devilkin 3 days to devilkin forever depending on the gravity. In any case: the player should be warn that this is the last warning before ban. If the player obviously don't measure the meaning of the avatar, the penalty can be reduced. |- ! Avatar | Inappropriate username | If needed, report author should explain why this is inappropriate. Username are inappropriate when this is obviously an insult for many people. Moderators shouldn't moderate a username that is insulting by accident in a language that is not widely used on BGA. | Ban from the website. Moderators should explain that the player is welcome with another username on BGA. Note that if the player has a long seniority on BGA (=many games played), moderators should be very cautious on the case: if no one noticed the username until now, it is probably not inappropriate. |- ! Table description | Inappropriate table description | If needed, report author should explain why this is inappropriate. Table description is inappropriate when it contains insults, insanity, or tend to discriminate a group of people. | Devilkin 3 days for small insults and insanity, Devilkin 30 days for real insults. Smaller penalty (ex: 1 day) can be applied if this was obviously a stupid joke. In any case: the player should be warn that this is the last warning before ban. |- ! Kingmaking | Kingmaking action during a game | The report must contains the table id, the exact move number, and the exact explanation why this is a kingmaking action. To declare a player guilty, the player must have played an obvious move against his own interest, and it has to be a good player (clues: his ELO, number of game played, ...). As the difference between a mistake and a kingmaking action is very tight, one kingmaking report is not enough to find a player guilty: only players who regularly play against their interest should be found guilty. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Bug exploit | Exploit a known bug during a game | The bug must have been reported in the forum, and an exact reference to this bug must be in the report, with the table id and the move number. Minor bugs that can be considered as "variants" are NOT concerned (ex: pick 4 cards instead of 3). Bugs that violate the spirit of the game are concerned. | No penalty applies: if there is a bug that has been widely used for a game, a global ELO reset (or an ELO adjustment for players who exploited the bug) will be done. |- ! Private information | Some player gives some private information during the game | Players have the right to pretend things during a game. As these infos could be true or false, and could be exploited by other players too, moderators should be cautious when judging these cases. This case concerns players that had a strong intent to kill the pleasure of the game by giving some private information during the game. | -1 to -10 reputation penalty. Moderators should warn the player that this is a only small warning. Relapses can lead to higher reputation points penalties. |- ! Multiple accounts | A player is using multiple accounts (most of the time: to boost his ELO rank) | The report must contains the usernames of all intimidated accounts, and the reason why the report author think that multiple accounts are used. It is very difficult to find out if different accounts corresponds to different person. For example: BGA accepts that multiple players plays from the same IP address, to allow people from the same family to play at the same table. In addition, some players are using multiple accounts, but these accounts are not playing together (which is acceptable). Strong tools are available for moderators to detect multiple accounts that are used the wrong way. | For secondary accounts: ban forever. For main account: ELO reseted to 1500 / ELO penalty for all games. |- ! Something else | Spam / advertising / inappropriate links | The player is using BGA for advertising. In particular: links not related to the purpose of the page, advertising for other online boardgaming websites, spam, ... | |- ! Something else | Translation stealing / vandalism | The player is writing inappropriate / poor quality translations (or writes a lot of very minor modifications to someone else translation) to gain free club membership, | Immediate ban from the website (to avoid vandalism). |- ! Something else | Player is slow to play | Players have the right to think as long as they do not go over their allotted time. If the player is slow on purpose, the good answer is: thumb down. | No penalty |- ! Something else | Player quit the game before the end | In this case, player automatically gets a ELO penalty + a red mark on his profile. There is no need to do something else. | No penalty |- |} 91f7c78eaee74c38df5fac94a13f00cfe84fbd06 Tutorial reversi 0 57 1047 937 2014-01-09T23:31:57Z Deluxe flame 4630 /* Make the squares appears */ wikitext text/x-wiki == Introduction == Using this tutorial, you can build a complete working game on the BGA environment: Reversi. Before you read this tutorial, you must: * Read the overall presentations of the BGA Framework ([[Studio|see here]]). * Know the [http://en.wikipedia.org/wiki/Reversi#Rules rules of Reversi]. * Know the languages used on BGA: PHP, SQL, HTML, CSS, Javascript. == Create your first game == With the initial skeleton of code provided initially, you can already start a game from the BGA Studio. For now, we are going to work with 1 player only. Most of the time this is simpler to proceed with only one player during the early phase of development of your game, as it's easy and fast to start/stop games. Thus, you can start a "Reversi" game, and arrive on a void, empty game. Yeah. == Let it look like Reversi == It's always a good idea to start with a little bit of graphic work. Why? Because this helps to figure out how the final game will be, and issues that may appear later. Be careful designing the layout of your game: you must always keep in mind that players with a 1024px screen width must be able to play. Usually, it means that the width of the play area can be 750px (in the worst case). For Reversi, it's useless to have a 750x750px board - much too big, so we choose this one which fit perfectly (536x528): [[File:Board.jpg]] Note that we are using a jpg file. Jpg are lighter than png, so faster to load. Later we are going to use PNGs for discs for transparency purpose. Now, let's make it appears on our game: * upload board.jpg in your "img/" directory. * edit "reversi_reversi.tpl" to add a 'div' for your board: <pre> <div id="board"> </div> </pre> * edit your reversi.css file to transform it into a visible board: #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); } Refresh your page. Here's your board: [[File:reversi1.jpg]] == Make the squares appears == Now, what we need is to create some invisible HTML elements where squares are. These elements will be used as position references for white and black discs. Obviously, we need 64 squares. To avoid writing 64 'div' elements on our template, we are going to use the "block" feature. Let's modify our template like this: <pre> <div id="board"> <!-- BEGIN square --> <div id="square_{X}_{Y}" class="square" style="left: {LEFT}px; top: {TOP}px;"></div> <!-- END square --> </div> </pre> As you can see, we created a "square" block, with 4 variable elements: X, Y, LEFT and TOP. Obviously, we are going to use this block 64 times during page load. Let's do it in our "reversi.view.php" file: <pre> $this->page->begin_block( "reversi_reversi", "square" ); $hor_scale = 64.8; $ver_scale = 64.4; for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $this->page->insert_block( "square", array( 'X' => $x, 'Y' => $y, 'LEFT' => round( ($x-1)*$hor_scale+10 ), 'TOP' => round( ($y-1)*$ver_scale+7 ) ) ); } } </pre> Note: as you can see, squares in our "board.jpg" files does not have an exact width/height in pixel, and that's the reason we are using floating point numbers here. Now, to finish our work and check if everything works fine, we are going to style our square a little bit in our CSS stylesheet: <pre> #board { width: 536px; height: 528px; background-image: url('img/board.jpg'); position: relative; } .square { width: 62px; height: 62px; position: absolute; background-color: red; } </pre> Explanations: * With "position: relative" on board, we ensure square elements are positioned relatively to board. * For the test, we use a red background color for the square. This is a useful tip to figure out if everything is fine with invisible elements. Let's refresh and check our our (beautiful) squares: [[File:reversi2.jpg]] == The discs == Now, our board is ready to receive some disc tokens! At first, we introduce a new 'div' element as a child of "board" to host all these tokens (in our template): <pre> <!-- END square --> <div id="tokens"> </div> </div> </pre> Then, let's introduce a new piece of art with the discs. We need some transparency here so we are using a png file: [[File:tokens.png]] Important: we are using ONE file for both discs. It's really important that you use a minimum number of graphic files for your game with this "CSS sprite" technique, because it makes the game loading faster and more reliable. [http://www.w3schools.com/css/css_image_sprites.asp Read more about CSS sprites]. Now, let's separate the disc with some CSS stuff: <pre> .token { width: 56px; height: 56px; position: absolute; background-image: url('img/tokens.png'); } .tokencolor_ffffff { background-position: 0px 0px; } .tokencolor_000000 { background-position: -56px 0px; } </pre> With this CSS code, we apply the classes "token" and "tokencolor_ffffff" to a div element and we've got a white token. Yeah. Note the "position: absolute" which allows us to position tokens on the board and make them "slide" to their positions. Now, let's make a first token appear on our board. Disc tokens are not visible at the beginning of the game: they appear dynamically during the game. For this reason, we are going to make them appear from our Javascript code, with a BGA Framework technique called "JS template". In our template file (reversi_reversi.tpl), let's create the piece of HTML needed to display our token: <pre> <script type="text/javascript"> // Templates var jstpl_token='<div class="token tokencolor_${color}" id="token_${x_y}"></div>'; </script> </pre> Note: we already created the "templates" section for you in the game skeleton. As you can see, we defined a JS template named "jstpl_token" with a piece of HTML and two variables: the color of the token and its x/y coordinates. Note that the syntax of the argument is different for template block variables (brackets) and JS template variables (dollar and brackets). Now, let's create a method in our Javascript code that will make a token appear on the board, using this template: <pre> addTokenOnBoard: function( x, y, player ) { dojo.place( this.format_block( 'jstpl_token', { x_y: x+'_'+y, color: this.gamedatas.players[ player ].color } ) , 'tokens' ); this.placeOnObject( 'token_'+x+'_'+y, 'overall_player_board_'+player ); this.slideToObject( 'token_'+x+'_'+y, 'square_'+x+'_'+y ).play(); }, </pre> At first, with "dojo.place" and "this.format_block" methods, we create a HTML piece of code and insert it as a new child of "tokens" div element. Then, with BGA "this.placeOnObject" method, we place this element over the panel of some player. Immediately after, using BGA "this.slidetoObject" method, we make the disc slide to the "square" element, its final destination. Note: don't forget to call the "play()", otherwise the token remains at its original location. Note: note that during all the process, the parent of the new disc HTML element will remain "tokens". placeOnObject and slideToObject methods are only moving the position of elements on screen, and they are not modifying the HTML tree. Before we can show a token, we need to set the player colors in the setupNewGame function in reversi.game.php: <pre> $default_colors = array( "ffffff", "000000" ); </pre> Now, to test if everything works fine, just call "this.addTokenOnBoard( 2, 2, <your_player_id> )" in your "setup" Javascript method, and reload the page. A token should appear and slide immediately to its position, like this: [[File:reversi3.jpg]] == The database == We did most of the client-side programming, so let's have a look on the other side now. To design the database model of our game, the best thing to do is to follow the "Go to game database" link at the bottom of our game, to access the database directly with a [http://www.phpmyadmin.net/ PhpMyAdmin] instance. Then, you can create the tables you need for your table (do not remove existing tables!), and report every SQL command used in your "dbmodel.sql" file. [[File:reversi4.jpg]] The database model of Reversi is very simple: just one table with the squares of the board. In our dbmodel.sql, we have this: <pre> CREATE TABLE IF NOT EXISTS `board` ( `board_x` smallint(5) unsigned NOT NULL, `board_y` smallint(5) unsigned NOT NULL, `board_player` int(10) unsigned DEFAULT NULL, PRIMARY KEY (`board_x`,`board_y`) ) ENGINE=InnoDB; </pre> Now, a new database with a "board" table will be created each time we start a Reversi game. This is why after modifying our dbmodel.sql it's a good time to stop & start again our game. == Setup the initial game position == The "setupNewGame" method of our reversi.game.php is called during initial setup: this is the place to initialize our data and to place the initial tokens on the board (initially, there are 4 tokens on the board). Let's do this: <pre> // Init the board $sql = "INSERT INTO board (board_x,board_y,board_player) VALUES "; $sql_values = array(); list( $blackplayer_id, $whiteplayer_id ) = array_keys( $players ); for( $x=1; $x<=8; $x++ ) { for( $y=1; $y<=8; $y++ ) { $token_value = "NULL"; if( ($x==4 && $y==4) || ($x==5 && $y==5) ) // Initial positions of white player $token_value = "'$whiteplayer_id'"; else if( ($x==4 && $y==5) || ($x==5 && $y==4) ) // Initial positions of black player $token_value = "'$blackplayer_id'"; $sql_values[] = "('$x','$y',$token_value)"; } } $sql .= implode( $sql_values, ',' ); self::DbQuery( $sql ); // Active first player self::activeNextPlayer(); </pre> As you can see, we create one table entry for each square, with a "NULL" value which means "empty square". Of course, for 4 specific squares, we place an initial token. At the end, we call activeNextPlayer to make the first player active at the beginning of the game. Now, we need to make these tokens appear on the client side. To achieve this, the first step is to return the token positions with our "getAllDatas" PHP method (called during each page reload): <pre> // Get reversi board token $result['board'] = self::getObjectListFromDB( "SELECT board_x x, board_y y, board_player player FROM board WHERE board_player IS NOT NULL" ); </pre> As you can see, we are using the BGA framework "getObjectListFromDB" method that formats the result of this SQL query in a PHP array with x, y and player attributes. The last thing we need to do is to process this array client side, and place a disc token on the board for each array item. Of course, we are doing this is our Javascript "setup" method: <pre> for( var i in gamedatas.board ) { var square = gamedatas.board[i]; if( square.player !== null ) { this.addTokenOnBoard( square.x, square.y, square.player ); } } </pre> As you can see, our "board" entry created in "getAllDatas" can be used here as "gamedatas.board" in our Javascript. We are using our previously developed "addTokenOnBoard" method. Reload... and here we are: [[File:reversi5.jpg]] It starts to smell Reversi here... == The game state machine == Now, let's stop our game again, because we are going to start the core game logic. You already read the "Focus on BGA game state machine", so you know that this is the heart of your game logic. For reversi, it's very simple although. Here's a diagram of our game state machine: [[File:reversi6.jpg]] And here's our "states.inc.php", according to this diagram: <pre> $machinestates = array( 1 => array( "name" => "gameSetup", "description" => clienttranslate("Game setup"), "type" => "manager", "action" => "stGameSetup", "transitions" => array( "" => 10 ) ), 10 => array( "name" => "playerTurn", "description" => clienttranslate('${actplayer} must play a disc'), "descriptionmyturn" => clienttranslate('${you} must play a disc'), "type" => "activeplayer", "args" => "argPlayerTurn", "possibleactions" => array( 'playDisc' ), "transitions" => array( "playDisc" => 11, "zombiePass" => 11 ) ), 11 => array( "name" => "nextPlayer", "type" => "game", "action" => "stNextPlayer", "updateGameProgression" => true, "transitions" => array( "nextTurn" => 10, "cantPlay" => 11, "endGame" => 99 ) ), 99 => array( "name" => "gameEnd", "description" => clienttranslate("End of game"), "type" => "manager", "action" => "stGameEnd", "args" => "argGameEnd" ) ); </pre> Now, let's create in reversi.game.php the methods that are declared in this game states description file: * argPlayerTurn * stNextPlayer ... and start a new Reversi game. As you can see on the screen capture below, the BGA framework makes the game jump to our first game state "playerTurn" right after the initial setup. That's why the status bar contains the description of playerTurn state ("XXXX must play a disc"): [[File:reversi7.jpg]] == The rules == Now, what we would like to do is to indicate to the current player where it is allowed to play. The idea is to build a "getPossibleMoves" PHP method that return a list of coordinates where it is allowed to play. This method will be used in particular: * As we just said, to help the player to see where he can play. * When the player play, to check if he has the right to play here. This is pure PHP programming here, and there's no special things from the BGA framework that can be used. This is why we won't go into details here. The overall idea is: * Create a "getTurnedOverDiscs(x,y)" method that return coordinates of discs that would be turned over if a token would be played at x,y. * Loop through all free squares of the board, call the "getTurnedOverDiscs" method on each of them. If at least 1 token is turned over, this is a valid move. One important thing to keep in mind is the following: making a database query is slow, so please don't load the entire game board with a SQL query multiple time. In our implementation, we load the entire board once at the beginning of "getPossibleMoves", and then pass the board as an argument to all methods. If you want to look into details, please look at the "utility method" sections of reversi.game.php. == Display allowed moves == Now, what we want to do is highlight squares where player can place a disc. To do this, we are using the "argPlayerTurn" method. This method is called each time we enter into "playerTurn" game state, and its result is transfered automatically to the client-side: <pre> function argPlayerTurn() { return array( 'possibleMoves' => self::getPossibleMoves( self::getActivePlayerId() ) ); } </pre> We are of course using the "getPossibleMoves" method we just developed. Now, let's go to the client side to use the data returned by the method above. We are using the "onEnteringState" Javascript method that is called each time we enter into a new game state: <pre> onEnteringState: function( stateName, args ) { console.log( 'Entering state: '+stateName ); switch( stateName ) { case 'playerTurn': this.updatePossibleMoves( args.args.possibleMoves ); break; } }, </pre> So, when we are entering into "playerTurn" game state, we are calling our "updatePossibleMoves" method. This method looks like this: <pre> updatePossibleMoves: function( possibleMoves ) { // Remove current possible moves dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); for( var x in possibleMoves ) { for( var y in possibleMoves[ x ] ) { // x,y is a possible move dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); } } this.addTooltipToClass( 'possibleMove', '', _('Place a disc here') ); }, </pre> The idea here is that we've created a CSS class ("possibleMove") that can be applied to a "square" element to highlight it: <pre> .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } </pre> At first, we remove all "possibleMove" classes currently applied with the very useful combination of "dojo.query" and "removeClass" method. Then we loop through all possible moves our PHP "updatePossibleMoves" function created for us, and add the "possibleMove" class to each corresponding square. Finally, we use the BGA framework "addTooltipToClass" method to associate a tooltip to all those highlighted squares so that players can understand their meaning. And here we are: [[File:reversi8.jpg.jpg]] == Let's play == From now, it's better to restart a game with 2 players, because we are going to implement a complete Reversi turn. The summary of what we are going to do is: * When we click on a "possibleMove" square, send the move to the server. * Server side, check the move is correct, apply Reversi rules and jump to next player. * Client side, change the disc position to reflect the move. Thus, what we do first is associate each click on a square to one of our method. We are doing this in our Javascript "setup" method: <pre> dojo.query( '.square' ).connect( 'onclick', this, 'onPlayDisc' ); </pre> Note the use of the "dojo.query" method to get all HTML elements with "square" class in just one function call. Now, our "onPlayDisc" method is called each time someone clicks on a square. Here's our "onPlayDisc" method below: <pre> onPlayDisc: function( evt ) { // Stop this event propagation dojo.stopEvent( evt ); // Get the cliqued square x and y // Note: square id format is "square_X_Y" var coords = evt.currentTarget.id.split('_'); var x = coords[1]; var y = coords[2]; if( ! dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { // This is not a possible move => the click does nothing return ; } if( this.checkAction( 'playDisc' ) ) // Check that this action is possible at this moment { this.ajaxcall( "/reversi/reversi/playDisc.html", { x:x, y:y }, this, function( result ) {} ); } }, </pre> What we do here is: * We stop the propagation of the Javascript "onclick" event. Otherwise, it can lead to random behavior so it's always a good idea. * We get the x/y coordinates of the square by using "evt.currentTarget.id". * We check that clicked square has the "possibleMove" class, otherwise we know for sure that we can't play there. * We check that "playDisc" action is possible, according to current game state (see "possibleactions" entry in our "playerTurn" game state defined above). This check is important to avoid issues if a player double clicks on a square. * Finally, we make a call to the server using BGA "ajaxcall" method with argument x and y. Now, we have to manage this "playDisc" action on server side. At first, we introduce a "playDisc" entry point in our "reversi.action.php": <pre> public function playDisc() { self::setAjaxMode(); $x = self::getArg( "x", AT_posint, true ); $y = self::getArg( "y", AT_posint, true ); $result = $this->game->playDisc( $x, $y ); self::ajaxResponse( ); } </pre> As you can see, we get the 2 arguments x and y from the javascript call, and call a corresponding "playDisc" method in our game logic. Now, let's have a look of this playDisc method: <pre> function playDisc( $x, $y ) { // Check that this player is active and that this action is possible at this moment self::checkAction( 'playDisc' ); </pre> ... at first, we check that this action is possible according to current game state (see "possible action"). We already did it on client side, but it's important to do it on server side too (otherwise it would be possible to cheat). <pre> // Now, check if this is a possible move $board = self::getBoard(); $player_id = self::getActivePlayerId(); $turnedOverDiscs = self::getTurnedOverDiscs( $x, $y, $player_id, $board ); if( count( $turnedOverDiscs ) > 0 ) { // This move is possible! </pre> ...now, we are using the "getTurnedOverDiscs" method again to check that this move is possible. <pre> // Let's place a disc at x,y and return all "$returned" discs to the active player $sql = "UPDATE board SET board_player='$player_id' WHERE ( board_x, board_y) IN ( "; foreach( $turnedOverDiscs as $turnedOver ) { $sql .= "('".$turnedOver['x']."','".$turnedOver['y']."'),"; } $sql .= "('$x','$y') ) "; self::DbQuery( $sql ); </pre> ... we update the database to change the color of all turned over disc + the disc we just placed. <pre> // Update scores according to the number of disc on board $sql = "UPDATE player SET player_score = ( SELECT COUNT( board_x ) FROM board WHERE board_player=player_id )"; self::DbQuery( $sql ); // Statistics self::incStat( count( $turnedOverDiscs ), "turnedOver", $player_id ); if( ($x==1 && $y==1) || ($x==8 && $y==1) || ($x==1 && $y==8) || ($x==8 && $y==8) ) self::incStat( 1, 'discPlayedOnCorner', $player_id ); else if( $x==1 || $x==8 || $y==1 || $y==8 ) self::incStat( 1, 'discPlayedOnBorder', $player_id ); else if( $x>=3 && $x<=6 && $y>=3 && $y<=6 ) self::incStat( 1, 'discPlayedOnCenter', $player_id ); </pre> ... now, we update both player score by counting all disc, and we manage game statistics. <pre> // Notify self::notifyAllPlayers( "playDisc", clienttranslate( '${player_name} plays a disc and turns over ${returned_nbr} disc(s)' ), array( 'player_id' => $player_id, 'player_name' => self::getActivePlayerName(), 'returned_nbr' => count( $turnedOverDiscs ), 'x' => $x, 'y' => $y ) ); self::notifyAllPlayers( "turnOverDiscs", '', array( 'player_id' => $player_id, 'turnedOver' => $turnedOverDiscs ) ); $newScores = self::getCollectionFromDb( "SELECT player_id, player_score FROM player", true ); self::notifyAllPlayers( "newScores", "", array( "scores" => $newScores ) ); </pre> ... then we notify about all these changes. We are using for that 3 notifications ('playDisc', 'turnOverDiscs' and 'newScores' that we are going to implement on client side later). Note that the description of the 'playDisc' notification will be logged in the game log. <pre> // Then, go to the next state $this->gamestate->nextState( 'playDisc' ); } else throw new feException( "Impossible move" ); } </pre> ... finally, we jump to the next game state if everything goes fine ('playDisc' is also the name of a transition in the 'playerTurn' game state description above). To make the statistics work, we have to initialize them in state.inc.php: <pre> // Statistics existing for each player "player" => array( "discPlayedOnCorner" => array( "id"=> 10, "name" => totranslate("Discs played on a corner"), "type" => "int" ), "discPlayedOnBorder" => array( "id"=> 11, "name" => totranslate("Discs played on a border"), "type" => "int" ), "discPlayedOnCenter" => array( "id"=> 12, "name" => totranslate("Discs played on board center part"), "type" => "int" ), "turnedOver" => array( "id"=> 13, "name" => totranslate("Number of discs turned over"), "type" => "int" ) ) </pre> A last thing to do on the server side is to activate the next player when we enter the "nextPlayer" game state: <pre> function stNextPlayer() { // Activate next player $player_id = self::activeNextPlayer(); self::giveExtraTime( $player_id ); $this->gamestate->nextState( 'nextTurn' ); } </pre> Now, when we play a disc, the rules are checked and the disc appears in the database. [[File:reversi9.jpg]] Of course, as we don't manage notifications on client side, we need to press F5 after each move to see the changes on the board. == Make the move appear automatically == Now, what we have to do is process the notifications sent by the server and make the move appear on the interface. In our "setupNotifications" method, we register 2 methods for the 2 notifications we created at the previous step ('playDisc' and 'turnOverDiscs'): <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); dojo.subscribe( 'turnOverDiscs', this, "notif_turnOverDiscs" ); this.notifqueue.setSynchronous( 'turnOverDiscs', 1500 ); </pre> As you can see, we associate our 2 notifications with 2 methods with the "notif_" prefix. At the same time, we define these notifications as "synchronous", with a duration in millisecond (500 for the first one, and 1500 for the second one). It tells the user interface to wait some time after executing the notification, to let the animation end before starting the next notification. In our specific case, the animation will be the following: * Make a disc slide from the player panel to its place on the board * (wait 500ms) * Make all turned over discs blink (and of course turned them over) * (wait 1500ms) * Let the next player play. Let's have a look now on the "playDisc" notification handler method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addTokenOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> No surprise here, we re-used some existing stuff to: * Remove the highlighted squares. * Add a new disc on board, coming from player panel. Now, here's the method that handles the turnOverDiscs notification: <pre> notif_turnOverDiscs: function( notif ) { // Get the color of the player who is returning the discs var targetColor = this.gamedatas.players[ notif.args.player_id ].color; // Make these discs blink and set them to the specified color for( var i in notif.args.turnedOver ) { var token = notif.args.turnedOver[ i ]; // Make the token blink 2 times var anim = dojo.fx.chain( [ dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ), dojo.fadeOut( { node: 'token_'+token.x+'_'+token.y, onEnd: function( node ) { // Remove any color class dojo.removeClass( node, [ 'tokencolor_000000', 'tokencolor_ffffff' ] ); // ... and add the good one dojo.addClass( node, 'tokencolor_'+targetColor ); } } ), dojo.fadeIn( { node: 'token_'+token.x+'_'+token.y } ) ] ); // end of dojo.fx.chain // ... and launch the animation anim.play(); } }, </pre> The list of the discs to be turned over has been made available by our server side code in "notif.args.turnedOver" (see previous paragraph). We loop through all these discs, and create a complex animation using dojo.Animation for each of them. The complete documentation on dojo animations [http://dojotoolkit.org/documentation/tutorials/1.6/animation/ can be found here]. In few words: we create a chain of 4 animations to make the disc fade out, fade in, fade out again, and fade in again. At the end of the second fade out, we change the color of the disc. Finally, we launch the animation with "play()". 9c6a8cb2c794566964cbaebf6757a4a8d58e53f8 Grade 0 30 1048 712 2014-01-14T02:59:12Z Tim robinson 4708 /* All grades */ wikitext text/x-wiki Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a mortal. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the angel grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to devilkin or demon. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraphim can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherubim check that Seraphim are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. * '''Demon''': due to a major terms of use violation, this player is not allowed to do anything on the website during a period of time (or forever ...) 5953d5338df3e4a588747647198989d0eede4340 Rating 0 16 1049 184 2014-01-15T08:22:08Z Tim robinson 4708 /* Okay, but I want to know the formula ! */ wikitext text/x-wiki [[Category:Help]] == What is ELO rating ? == Your ELO rating for a game is your level at this game. It's a 4 digits number followed by sign http://fr.boardgamearena.com/theme/img/common/rank.png. Example: 1648 http://fr.boardgamearena.com/theme/img/common/rank.png. If you never play a game on BGA, your initial rating is 1500 . Each time you win a game your rating increases, and it decreases each time you lose a game. == How many points do I gain / lose for each game ? == ELO points gains / losses depend on the level of your opponents, and your rank at the end of the game. Some examples: * If you end the game at a better place than a player with a higher ELO, you gain a lot of points. * If you end the game at a better place than a player with a lower ELO, you gain less points. * If you end the game at a worse place than a player with a lower ELO, you lose a lot points. * And so on... == Some advice == * There's no use beating players a lot weaker than you (not many points to win). Try to find opponents of your level: it's both more fun and more good for your ELO. * The game rank is very important to calculate the ELO. This is the reason it is important to continue fighting for the second place during the game if you think you can't win. * The number of ELO points you can gain/lose during a game increases with the number of players. The maximum is reached when there is the "recommended number of players". Try to play games with this recommended number of players. * When you are the first player to quit a game in progress, you lose as many points as if you would finish the game at the last place, plus an additional penalty of 10 points. Even if you are in a difficult situation, your interest is to play the game until the end - or concede the victory to your opponent. == How is my ELO ranking computed ? == The BGA ELO system is directly based on the standard [http://en.wikipedia.org/wiki/Elo_rating_system ELO rating system], in use for chess. The ELO system main principle is the followin: the ELO points difference between 2 players determines the probability of each of them to win the encounter. If two players has the same ELO, their probability to win are 50/50. If one player has 400 more points than the other, his probability to win is 90%. ELO points gains and losses after each game tend to adjust ELO ratings of each player in order this principle is applied. ELO system on BGA has some specificity: * During your first 30 games, your ELO rating is more "elastic": you can win (or lose) more points at each game. This way, your ELO rating converge faster to you "natural" rating. * Original ELO rating system has been designed for 2 player games. For games with more than 2 players, BGA considers (for the ELO rating) that you win a 2-player game against each opponent after you (in game rank) and that you lose a 2-player game against each opponent before you (in game rank). * Games with more than 2 players last longer. For this reason, there's more points to win (or lose) in such games... as long as the number of players does not exceed the "advised number of players" for that game. == Okay, but I want to know the formula ! == The formula is exactly one's used by the ELO rating system, with the following adjustments: * First of all, if someone left a game for any reason this game is not taken into account by the ELO rating system. * K=60 for the first 30 games, K=40 afterwards. * K is multiply by (N/2) for N-players games. If N exceed the "advised number of players" for this game, K is multiply by (A/2), where A is the "advised number of players". * If 2 players has a rating difference greater than 600, we consider that their ELO difference is 600. == Good players, Experts, Masters == * Good players: > 1600 ELO * Experts: > 1800 ELO * Masters: > 2000 ELO be3b5bffa0a8ad60134ce93dc6a4f93df0db1ed0 Gamehelptournay 0 143 1050 2014-02-04T15:31:51Z Pikiou 1872 No rules were added, so I took the opportunity to thank Nosnhoj Kräm ^^ wikitext text/x-wiki Thanks to Nosnhoj Kräm (Eeeville) for his Tournay card descriptions used in the BGA adaptation! ab25f7264e6489f116b85b3b93a28bbdc6055ce5 Tiedosto:BGA transparent horizontal logo.png 6 144 1051 2014-02-12T20:06:45Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:BGA transparent horizontal white logo.png 6 145 1052 2014-02-12T20:09:09Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:BGA transparent vertical logo.png 6 146 1053 2014-02-12T20:10:18Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Logos 0 147 1054 2014-02-12T20:10:37Z Sourisdudesert 1 Created page with "On this page you can find brand assets for Board Game Arena. Please use "right click + save image as" to download needed images. == BGA transparent horizontal logo == (sho..." wikitext text/x-wiki On this page you can find brand assets for Board Game Arena. Please use "right click + save image as" to download needed images. == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] b7180517559277c310a87e737ff30450271c7977 1055 1054 2014-02-12T20:11:10Z Sourisdudesert 1 wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] e03e41f9b20d1d450803ebf772d56f59f8ddf39f 1060 1055 2014-02-12T20:13:07Z Sourisdudesert 1 wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] == BGA icon == 16x16: [[File:BGA logo 16.png]] 75x75: [[File:BGA logo 75.png]] 128x128: [[File:BGA logo 512.png]] 512x512: [[File:BGA logo 512.png]] 3c03e32262b480d49428c87ea5980a08ec25b20d 1061 1060 2014-02-12T20:13:24Z Sourisdudesert 1 /* BGA icon */ wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] == BGA icon == 16x16: [[File:BGA logo 16.png]] 75x75: [[File:BGA logo 75.png]] 128x128: [[File:BGA logo 128.png]] 512x512: [[File:BGA logo 512.png]] a1c641bd1ba13393eec05921f99ef308d7218ec8 1071 1061 2014-02-12T21:05:52Z Sourisdudesert 1 wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. = BGA buttons = You can use buttons below as links to BGA website: == English == [[File:play_games_online_en.png]] == French == [[File:play_games_online_fr.png]] == German == [[File:play_games_online_de.png]] == Spanish == [[File:play_games_online_es.png]] == Italian == [[File:play_games_online_it.png]] == Chinese (traditional) == [[File:play_games_online_zh.png]] == Chinese (simplified) == [[File:play_games_online_zh_cn.png]] == Japanese == [[File:play_games_online_ja.png]] = BGA logos = == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] == BGA icon == 16x16: [[File:BGA logo 16.png]] 75x75: [[File:BGA logo 75.png]] 128x128: [[File:BGA logo 128.png]] 512x512: [[File:BGA logo 512.png]] 3006bfb0c058b927e3507b2334b8c0f832dfd092 1074 1071 2014-02-12T21:08:32Z Sourisdudesert 1 /* English */ wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. = BGA buttons = You can use buttons below as links to BGA website: == Language independent == [[File:bga_button.png]] == English == Generic: [[File:play_games_online_en.png]] Play it online: [[File:play_it_on_bga_en.png]] == French == [[File:play_games_online_fr.png]] == German == [[File:play_games_online_de.png]] == Spanish == [[File:play_games_online_es.png]] == Italian == [[File:play_games_online_it.png]] == Chinese (traditional) == [[File:play_games_online_zh.png]] == Chinese (simplified) == [[File:play_games_online_zh_cn.png]] == Japanese == [[File:play_games_online_ja.png]] = BGA logos = == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] == BGA icon == 16x16: [[File:BGA logo 16.png]] 75x75: [[File:BGA logo 75.png]] 128x128: [[File:BGA logo 128.png]] 512x512: [[File:BGA logo 512.png]] e72e3c1053edce9fbbcf1c3b2be00fd1d1d94bc5 1075 1074 2014-02-12T21:14:22Z Sourisdudesert 1 /* BGA buttons */ wikitext text/x-wiki On this page you can find brand assets for Board Game Arena and easy way to create links to BGA. Please use "right click + save image as" to download needed images. = BGA buttons = You can use buttons below as links to BGA website. If you need a button that is not on the list please contact us (contact@boardgamearena.com). == Language independent == [[File:bga_button.png]] == English == Generic: [[File:play_games_online_en.png]] Play it online: [[File:play_it_on_bga_en.png]] == French == [[File:play_games_online_fr.png]] == German == [[File:play_games_online_de.png]] == Spanish == [[File:play_games_online_es.png]] == Italian == [[File:play_games_online_it.png]] == Chinese (traditional) == [[File:play_games_online_zh.png]] == Chinese (simplified) == [[File:play_games_online_zh_cn.png]] == Japanese == [[File:play_games_online_ja.png]] = BGA logos = == BGA transparent horizontal logo == (should be used on a white background): [[File:BGA transparent horizontal logo.png]] == BGA transparent horizontal white logo == (should be used on a black background): [[File:BGA transparent horizontal white logo.png]] == BGA transparent vertical logo == [[File:BGA_transparent_vertical_logo.png]] == BGA icon == 16x16: [[File:BGA logo 16.png]] 75x75: [[File:BGA logo 75.png]] 128x128: [[File:BGA logo 128.png]] 512x512: [[File:BGA logo 512.png]] 06b065e7ed8f5184b92d12ca3bc0b6678a1131da Tiedosto:BGA logo 16.png 6 148 1056 2014-02-12T20:11:45Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:BGA logo 75.png 6 149 1057 2014-02-12T20:11:53Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:BGA logo 128.png 6 150 1058 2014-02-12T20:12:01Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:BGA logo 512.png 6 151 1059 2014-02-12T20:12:08Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online de.png 6 152 1062 2014-02-12T21:01:39Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online en.png 6 153 1063 2014-02-12T21:01:50Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online es.png 6 154 1064 2014-02-12T21:02:00Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online fr.png 6 155 1065 2014-02-12T21:02:08Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 1066 1065 2014-02-12T21:02:18Z Sourisdudesert 1 uploaded a new version of &quot;[[File:Play games online fr.png]]&quot; wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online it.png 6 156 1067 2014-02-12T21:02:28Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online ja.png 6 157 1068 2014-02-12T21:02:37Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online zh.png 6 158 1069 2014-02-12T21:02:51Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play games online zh cn.png 6 159 1070 2014-02-12T21:03:01Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Bga button.png 6 160 1072 2014-02-12T21:06:36Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Tiedosto:Play it on bga en.png 6 161 1073 2014-02-12T21:06:47Z Sourisdudesert 1 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Stock 0 97 1076 955 2014-02-19T21:10:34Z Pikiou 1872 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slided to, from and between your Stock controls smoothly d7f0b2e8d1b241b8e9bd10966b85aa7828974929 1080 1076 2014-02-24T18:05:47Z Een 3 wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slide to, from and between your Stock controls smoothly dae384c29525a1d69dd67e4501a77c283b59a801 1081 1080 2014-02-24T18:22:08Z Een 3 wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "<div>" element (a void <div> element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''setSelectionAppeareance( type )''' For each stock control, you can specify a selection highlighting type: * 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels). * 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item. * 'class': there will be an extra 'stockitem_selecte' css class added to the element when it is selected (and removed when unselected). You can override this class in the css file for your game. By default this class definition is: <pre> .stockitem_selected { border: 2px solid red ! important; } </pre> If you want to override it for example to change the border color add this in your <game>.css file: <pre> .stockitem_selected { border: 2px solid orange ! important; } </pre> NB: the 'class' highlighting type has not yet been deployed on the studio - 24/02/2014. This warning will be removed after the next upgrade. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the <div> container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slide to, from and between your Stock controls smoothly 201df1d5f75f0a9223a3d677a435868e6887bef3 1082 1081 2014-02-24T18:33:09Z Een 3 wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "div" element (a void div element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''setSelectionAppeareance( type )''' For each stock control, you can specify a selection highlighting type: * 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels). * 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item. * 'class': there will be an extra 'stockitem_selecte' css class added to the element when it is selected (and removed when unselected). You can override this class in the css file for your game. By default this class definition is: <pre> .stockitem_selected { border: 2px solid red ! important; } </pre> If you want to override it for example to change the border color add this in your <game>.css file: <pre> .stockitem_selected { border: 2px solid orange ! important; } </pre> NB: the 'class' highlighting type has not yet been deployed on the studio - 24/02/2014. This warning will be removed after the next upgrade. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the "div" container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slide to, from and between your Stock controls smoothly 9eb6c365aa835914a123b46b22e9f45ccc16c1b9 Gamehelptournay 0 143 1077 1050 2014-02-20T00:54:42Z Eeeville 1177 wikitext text/x-wiki Thanks to Mark Johnson (Eeeville) for his Tournay card descriptions used in the BGA adaptation! 6087e3fd7a00dd8eab30de396f2d7e435c85a04d 1090 1077 2014-03-08T10:00:53Z Drstuey 3099 started editing this page - will continue later wikitext text/x-wiki Thanks to Mark Johnson (Eeeville) for his Tournay card descriptions used in the BGA adaptation! The '''game ends''' when both of the following conditions are met, or if condition 1 is met by more than one player: # a player has filled 9 spaces with at least 2 visible prestige buildings. # one Town Crier card more than the number of players has been revealed. The last round is triggered at the beginning of the start player's turn and each player gets a normal turn in this round. Then all players can choose a final card to play into their districts. Important notes: * You cannot score more than 12 victory points from the same prestige building. * If two players build the same prestige buildings there is only a single scoring. 94d79bbb06c68138ff9a1a1099f2978b9c760473 1091 1090 2014-03-09T09:53:41Z Drstuey 3099 details of prestige buildings wikitext text/x-wiki Thanks to Mark Johnson (Eeeville) for his Tournay card descriptions used in the BGA adaptation! The '''game ends''' when both of the following conditions are met, or if condition 1 is met by more than one player: # A player has filled 9 spaces with at least 2 visible prestige buildings. # One Town Crier card more than the number of players has been revealed. The last round is triggered at the beginning of the start player's turn and each player gets a normal turn in this round. Then all players can choose a final card to play into their districts. Important notes: * You cannot score more than 12 victory points from the same prestige building. * If two players build the same prestige building there is only a single scoring. ===Details of prestige buildings=== '''Yellow:''' *'''Belfry''': per set (1 yellow + 1 white + 1 red) of buildings or characters *'''City Hall''': per yellow citizen (2x) *'''Cloth Hall''': per yellow building or character (2x) *'''Mint''': per set of 4 deniers (2x) *'''Tour des Six''': per character (2x) '''White:''' *'''Cathedral''': only the player who builds it earns 8 PPs. *'''La Madeleine''': per prestige building (2x) *'''Saint-Brice''': per set (1 yellow + 1 white + 1 red) of buildings or characters *'''Saint-Jacques''': per white building or character (2x) *'''Saint-Nicolas''': per set of citizens you own (1 yellow +1 white +1 red) *'''Saint-Quentin''': per white citizen (2x) '''Red:''' *'''Pont des Trous''': per set (1 yellow + 1 white + 1 red) of citizens *'''Porte de la Vigne''': per red citizen (2x) *'''Tour d’Arras''': per white and yellow building (2x) *'''Tour Henry VIII''': per rampart built (2x) *'''Tour Saint-Georges''': per red card building or character (2x) aba76f1199acfd1fafbb145b912989a43e9bcb0c Faq 0 3 1078 1028 2014-02-22T19:51:24Z Een 3 wikitext text/x-wiki [[Category:Help]] == General questions == ===What is Board Game Arena?=== Board Game Arena (BGA) is an online board game platform. With BGA, you can play online and in real time against players from the whole world. A selection of various board games and card games is available. In order to play, the only thing you need is your browser! ===What do you mean by "real time play"?=== You can find many websites where you can play board games with a "turn based" approach: after each move, you have to wait for your opponents to connect to the website and play their own move. Most of the time, you have to wait a long time between each move, and games take a long time to finish (if they ever do). On Board Game Arena, your opponents are connected to the game platform all along the game, and they can see your moves and react to them immediately - as is the case in the real world. ===What do I need to play?=== Your browser ... and that's it! Board Game Arena does not require anything else on your computer. Thus, you can play from any computer, anywhere, anytime! In order to make this thing possible, we are using the very latest technologies available for the web. This is the reason you should play with the most up to date web browsers to enjoy an optimal BGA experience (read more about our [[Browser_support|browser policy]]). ===How much does it cost to play on BGA?=== Playing on BGA is free. You can also choose to help us by making a donation and becoming a [http://en.boardgamearena.com/#!club Board Game Arena club] member. ===What are the games available on BGA?=== Board Game Arena proposes a various selection of board and card games, but is specialized in modern ("European style") adult games. [http://en.boardgamearena.com/#!gamelist See available games]. ===What can I do if I have an issue with the website?=== Most of the issues one can encounter on this website should be solved with a simple web page refresh (or "F5"). If you experience persistent issues, you can browse the [http://forum.boardgamearena.com/viewforum.php?f=4 bug section in the forum], see if someone already reported the issue, and add a new bug report if that's not the case. Please take some time to describe the bug you are reporting in detail so that we can quickly reproduce and fix it. ===What is the legal status of this website?=== All the games currently available on this site have been licensed or otherwise authorized by copyright holders. Please be aware that games available on BGA can be removed at the copyright holders demand (for example: if they publish their own adaptation of the game). Board Game Arena would like to thank all game publishers and game designers who allow us to have their games here, thus making it possible for this website to exist. ===I'm a game publisher: why would I like to have my game on Board Game Arena?=== The main goal of Board Game Arena is to make it easy for people to discover and love new games. We think that there is only two kinds of players on Board Game Arena: those who already bought the games they are playing online, and those who dream to buy them soon! As the activity of this website contributes to increasing a game's popularity and thus generates game boxes sales in the "real world", we think it is beneficial to the boardgame community: people from all over the world can play online, we can have fun developing this game platform to the best of our ability, and game publishers get money from the box sales and build a special relationship with their players community. == Meeting players and starting games == ===I launched a new table but nobody is joining?=== Although Board Game Arena has been enjoying a growing popularity, it is still a young website. For the most popular games, you can find players at any time of the day. For other games, we advise you to come back at peak hour (around 22:00 CEST) if you can't find opponents at the moment. Tip: check the number of online/available players for a game before launching the table. ===I joined a game. When does the game start?=== Each table has an administrator, which is the table creator. It is the responsibility of the table administrator to start the game when he is satisfied with the number of players having joined the table. If you are the table administrator and can't launch the game, most of the time it's because there is not yet enough players joined the table. ===What is the meaning of the small color circle next to players names?=== * http://fr.boardgamearena.com/theme/img/status/online_.png : this player is active. He completed an action very recently. * http://fr.boardgamearena.com/theme/img/status/inactive_.png : this player is inactive. He is connected to the website but did not perform any action recently. * http://fr.boardgamearena.com/theme/img/status/offline_.png : this player is offline. ===What does the '% hits' statistic mean?=== % hits = number of victory points / number of games played Where number of victory points is the following: * When you win a 2 players game: 1 point. * When you win a 3 players game: 1.5 points. * When you win a 4 players game: 2 points. * etc. This way, "50%" means that you win 50% of 2 players game, or 33% of 3 players game, etc. == During the game == ===What is the meaning of the icons next to players names?=== * http://fr.boardgamearena.com/theme/img/layout/active_player.gif : this player must make a move now. * http://fr.boardgamearena.com/theme/img/layout/active_player_clockalert.gif : this player must make a move now, and he has spent his reflexion time. Can someone wake him up? ;) * http://fr.boardgamearena.com/theme/img/layout/active_player_nonack.gif : this player must make a move now, but is probably not aware of the fact. If the situation does not evolve, it might be that this player has some connection issue. * http://fr.boardgamearena.com/theme/img/common/zombie.png : this player is a "zombie" (he left the game or has been fired from the game). The game goes on without this player. When the "normal" avatar of a player is displayed, it means that this player is waiting for his turn to play. ===A player has to make a move but he/she doesn't. What can I do?=== * First, please remember that this player has the right to think as long as he needs to about is next move... well as long as he still has some time left on the game clock, that is. * Then you can ask this player to confirm that he is still thinking about how to play. * If this player seems to be away, we advise you to wait a few minutes: this player may be experiencing some network problems or have left his computer temporarily. * If the player is not coming back, you can expel him from the game as soon as he is running out of time on the game clock for the current move (red bar) or if he goes over his alloted time to think by more than 3/4/5 minutes (depending on game speed). See Clock section for details. Important: in this case, no ELO points will be won by anybody for this game. ===What is the current progression of the game I'm playing?=== A percentage of progression is displayed on the top of the web page. There is also a progress bar at the bottom of the web page shows you the current progression of the game. ===What is forbidden on Board Game Arena?=== * Leaving a game in progress on purpose. * Taking an unnecessary and unreasonable amount of time at the end of game in an obvious losing situation. * Kingmaking: enabling another player to win on purpose while there is a better move to make for yourself. * Running out of time on the game clock. * Giving some piece of information about the current game situation that corrupts the normal game flow. * Communicating with another player about the game privately (ex: with MSN). * Provocation / triumphalism / defeatism ... anything which is not fair play. * Having a shocking avatar (no advertisement, no politics, ...) Going against these rules will affect your reputation. ===What is absolutely forbidden in Board Game Arena?=== * Creating multiple accounts and playing against oneself. * Insulting other players in any manner. Going against these rules can lead to the removal of your account and/or to the blacklisting of your IP. ===What if some player does something wrong?=== The Board Game Arena platform has been designed to encourage players to maintain good behavior. The reputation system allows you to distinguish between good and bad players. Then, it is up to you to decide whether to start a game with them or not. If you think some player has had bad behavior during a game, you can give him a at the end of the game. If you think some player committed some serious wrongdoing during a game (ex: insult), please report this player to us ("report this player" link from his profile or game result page) so that we can take appropriate measures. a1b0dbea7f9cfbcb77d9cbd8dcf02d3c641cb263 Game interface logic: yourgamename.js 0 88 1079 1009 2014-02-24T17:49:33Z Een 3 /* Scoring dialogs */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ).play(); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> You can also use three extra attributes in the parameter array for the notification: <pre> $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table, "header" => '<div>Some header</div>', "footer" => '<div>Some footer</div>', "closelabel" => clienttranslate( "Closing button label" ) ) ); </pre> *'''header''': the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors) *'''footer''': the content for this parameter will display after the table (no parsing for coloring the player names) *'''closelabel''': if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon). NB: this last parameter is not yet - 24/02/2014 - deployed on the studio, but can be used already and will take effect immediately with the next update. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. '''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 2a4582a2f5ebf347239e3414d23aaa78dee2bff1 1097 1079 2014-03-19T10:51:16Z Sourisdudesert 1 /* Animations */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> You can also use three extra attributes in the parameter array for the notification: <pre> $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table, "header" => '<div>Some header</div>', "footer" => '<div>Some footer</div>', "closelabel" => clienttranslate( "Closing button label" ) ) ); </pre> *'''header''': the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors) *'''footer''': the content for this parameter will display after the table (no parsing for coloring the player names) *'''closelabel''': if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon). NB: this last parameter is not yet - 24/02/2014 - deployed on the studio, but can be used already and will take effect immediately with the next update. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Reversi example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. '''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> a46f2086bc1c43aacfffaaf05d98154cc9a7954f 1099 1097 2014-03-24T17:43:35Z Sourisdudesert 1 /* Adding stuff to player's panel */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> You can also use three extra attributes in the parameter array for the notification: <pre> $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table, "header" => '<div>Some header</div>', "footer" => '<div>Some footer</div>', "closelabel" => clienttranslate( "Closing button label" ) ) ); </pre> *'''header''': the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors) *'''footer''': the content for this parameter will display after the table (no parsing for coloring the player names) *'''closelabel''': if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon). NB: this last parameter is not yet - 24/02/2014 - deployed on the studio, but can be used already and will take effect immediately with the next update. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Gomoku example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. '''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 3edb1b2de886971149bb7afa955790ee664eae63 Grade 0 30 1083 1048 2014-02-28T22:27:16Z Da man 5122 /* All grades */ wikitext text/x-wiki Board Game Arena is a friendly and respectful community of players. The moderation and grades system help us to ensure that the minority of players who disrespect the spirit of this website can't bother other players. When you register on Board Game Arena, you're a mortal. You can access most of the functionalities of this website, except some of them (ex: speaking on global chat). After a while, you are promoted to the angel grade. With this grade you are able to access all functionalities of the service. But, if you go against the Board Game Arena terms, you can be moderated and demoted to devilkin or demon. If you manage to become a well appreciated player, you can become a moderator with a superior grade: seraph or cherub. == All grades == * '''Mortal''': this is your grade when you registered on Board Game Arena. You can access almost all functionalities of the website (but you can't speak on general channel). * '''Angel''': this is the grade of regular players. To be promoted to this grade, you need: 3 days seniority, 3 games played and 3 positive reputation points. * '''Seraph''': this is the moderator grade. Seraphim can punish players who disrespect BGA terms of use: reputation penalties, inferior grade. * '''Cherub''': this is the super moderator grade. Cherubim check that Seraphim are fair and unbiased. * '''Archangel''': this is the Board Game Arena administrators' grade. * '''Devilkin''': due to a terms of use violation, this player is not allowed to speak on Board Game Arena (or publish anything) during a period of time. * '''Demon''': The grade we give to people when they post things in our forums that we don't really like to hear.. like... how bad administrators we are. Yes, we use censorship... if you don't like we ban you for some days (or forever ...) d61494b57e4426dac356ecc0045b330d4e5ef205 Gamehelppuertorico 0 23 1084 80 2014-03-02T10:26:22Z Een 3 wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game by collecting: * victory points earned for buildings * victory point tokens earned at the captain phase * conditional victory points earned at the end of the game for occupied large buildings In case of a tie, the player owning the most doubloons and goods is the winner. == Rules summary == The game is played over several rounds. At the start of a round, the governor selects a role, then each player in turn selects a role. At the end of the round, the governor token moves to the next player. When a role has been chosen, each player in turn plays the action for this role. Only the player who selected the role can use the role privilege. Available roles are the following: * Mayor: colonists set foot in the new world ! Privilege : the mayor can get one more colonist from the supply. Action : in turn, begining with the mayor, players get one colonist from the ship, until it is empty. Then players have to set colonists on their buildings and plantations. * Craftsman: goods are produced. Privilege : the craftsman can produce one more good of his choice. Action : produce goods for your occupied buildings and plantations. * Trader: goods are sold to the trading house. Privilege : the trader earns 1 extra doubloon if he sells. Action : sell one good the trading house doesn't have yet. * Settler : plantations settlement. Privilege : the settler can get a quarry instead of a plantation. Action : get a plantation chosen among those available. * Builder : buildings are bought and built. Privilege : the builder can get a building for 1 doubloon less than the regular price. Action : get a building chosen among those available. * Captain : victory points are won by shipping goods to the old world. Privilege : the captain earns one extra victory points if he ships some goods. Action : select a type of good to load on a cargo ship; 1 victory point is earned for each barrel of this type of good loaded on the ship. * Prospector : gold rush brings money ! Privilege : the prospector gets one doubloon from the bank. Action : none. Please also note that occupied violet buildings have modifier effects for role actions. End of game happens at the end of the round for which one of the following events comes to pass : * there is not enough colonists in the supply to refill the colonist ship at the end of the mayor phase * the last victory point token has been earned during the captain phase * at least one of the players built on his 12th and last free space in the city. == Playing online == Choosing a role : click on the role of your choice in the list on the right of your game board. * Mayor role : accept or refuse to get an extra colonist from the supply. Colonists are then automatically recruited by players. Click on a building or a plantation to setup a colonist on it. Click on a colonist to send it back to San Juan (the counter for colonists in San Juan is situated in your player panel on the right of the page). * Craftsman role : goods production is automated (counters for goods owned are in the player panel on the right of the page). Then, choose one extra good that you can produce as your privilege. * Trader role : choose a good that you want to sell by clicking on the appropriate counter in your player panel. * Settler role : click on the plantation (or quarry) that you want. Placement on your board is automated. * Builder role : click on the building that you want to buy. Placement on your board is automated. * Captain role : click on the cargo boat on which you want to ship some goods. Then choose the type of good that you want to ship by clicking on the appropriate counter in your player panel. At the end of the captain phase, if you have some goods to store (warehouse or windrose), click on the corresponding counter. Stored goods will be highlighted by a red frame. * Prospector role : doubloon is earned immediately. Tooltips are available to give information on roles and buildings by hovering over game elements with the mouse. == 'Balanced game' variant == This variant implements two game balance fixes giving each player an equal chance of winning as listed in the [http://en.wikipedia.org/wiki/Puerto_Rico_%28board_game%29 Puerto Rico (board game) Wikipedia page]. * The prices of the Factory and University buildings are swapped so that the Factory costs 8 doubloons and the University costs 7 doubloons * Any player that starts with a corn plantation starts with 1 doubloon less than the players that start with an indigo plantation. == Two players variant == Craftsman Angst : if this variant is selected, players alternate selecting a role as usual, until the Governor has selected three roles and the opponent has selected two (instead of three in a classic two players game), then the governor token goes to the opponent and a new round starts. So it avoids the consecutive selection of two roles by the same player that happens on a change of round with the classic two player game. This variant is described on [http://www.boardgamegeek.com/thread/679955/the-craftsman-angst-variant-the-most-popular-revis Board Game Geek]. '''Have a good game !''' 4af14d346dd971fe509fdbba835b794d1148c492 1085 1084 2014-03-02T10:27:41Z Een 3 /* Two players variant */ wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game by collecting: * victory points earned for buildings * victory point tokens earned at the captain phase * conditional victory points earned at the end of the game for occupied large buildings In case of a tie, the player owning the most doubloons and goods is the winner. == Rules summary == The game is played over several rounds. At the start of a round, the governor selects a role, then each player in turn selects a role. At the end of the round, the governor token moves to the next player. When a role has been chosen, each player in turn plays the action for this role. Only the player who selected the role can use the role privilege. Available roles are the following: * Mayor: colonists set foot in the new world ! Privilege : the mayor can get one more colonist from the supply. Action : in turn, begining with the mayor, players get one colonist from the ship, until it is empty. Then players have to set colonists on their buildings and plantations. * Craftsman: goods are produced. Privilege : the craftsman can produce one more good of his choice. Action : produce goods for your occupied buildings and plantations. * Trader: goods are sold to the trading house. Privilege : the trader earns 1 extra doubloon if he sells. Action : sell one good the trading house doesn't have yet. * Settler : plantations settlement. Privilege : the settler can get a quarry instead of a plantation. Action : get a plantation chosen among those available. * Builder : buildings are bought and built. Privilege : the builder can get a building for 1 doubloon less than the regular price. Action : get a building chosen among those available. * Captain : victory points are won by shipping goods to the old world. Privilege : the captain earns one extra victory points if he ships some goods. Action : select a type of good to load on a cargo ship; 1 victory point is earned for each barrel of this type of good loaded on the ship. * Prospector : gold rush brings money ! Privilege : the prospector gets one doubloon from the bank. Action : none. Please also note that occupied violet buildings have modifier effects for role actions. End of game happens at the end of the round for which one of the following events comes to pass : * there is not enough colonists in the supply to refill the colonist ship at the end of the mayor phase * the last victory point token has been earned during the captain phase * at least one of the players built on his 12th and last free space in the city. == Playing online == Choosing a role : click on the role of your choice in the list on the right of your game board. * Mayor role : accept or refuse to get an extra colonist from the supply. Colonists are then automatically recruited by players. Click on a building or a plantation to setup a colonist on it. Click on a colonist to send it back to San Juan (the counter for colonists in San Juan is situated in your player panel on the right of the page). * Craftsman role : goods production is automated (counters for goods owned are in the player panel on the right of the page). Then, choose one extra good that you can produce as your privilege. * Trader role : choose a good that you want to sell by clicking on the appropriate counter in your player panel. * Settler role : click on the plantation (or quarry) that you want. Placement on your board is automated. * Builder role : click on the building that you want to buy. Placement on your board is automated. * Captain role : click on the cargo boat on which you want to ship some goods. Then choose the type of good that you want to ship by clicking on the appropriate counter in your player panel. At the end of the captain phase, if you have some goods to store (warehouse or windrose), click on the corresponding counter. Stored goods will be highlighted by a red frame. * Prospector role : doubloon is earned immediately. Tooltips are available to give information on roles and buildings by hovering over game elements with the mouse. == 'Balanced game' variant == This variant implements two game balance fixes giving each player an equal chance of winning as listed in the [http://en.wikipedia.org/wiki/Puerto_Rico_%28board_game%29 Puerto Rico (board game) Wikipedia page]. * The prices of the Factory and University buildings are swapped so that the Factory costs 8 doubloons and the University costs 7 doubloons * Any player that starts with a corn plantation starts with 1 doubloon less than the players that start with an indigo plantation. == Two players variant == Craftsman Angst : if this variant is selected, players alternate selecting a role as usual, until the Governor has selected '''three''' roles and the opponent has selected '''two''' roles (instead of three in a classic two players game), then the governor token goes to the opponent and a new round starts. This variant avoids the consecutive selection of two roles by the same player which happens on a change of round with the classic two player game rules. This variant is described on [http://www.boardgamegeek.com/thread/679955/the-craftsman-angst-variant-the-most-popular-revis Board Game Geek]. '''Have a good game !''' 2d7aca9b12d032b70bc405a63a994ec943d7f481 Gamehelptokaido 0 134 1086 998 2014-03-03T00:10:48Z Sumner 5143 /* End of the journey */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf 6d73c686ae25a635291489a1902f4e9b3d406184 1087 1086 2014-03-03T07:13:23Z Sumner 5143 /* Expansions */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf == The New Encounters == This expansion adds 4 new Encounter cards. Details here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 7b1a4cb6dc6d2bbfafc545d8a32aff644af2f7ef 1088 1087 2014-03-03T07:20:31Z Sumner 5143 /* The New Encounters */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 830cea6507fbd647bbb61b278a115936ee4fbd2a 1089 1088 2014-03-03T07:26:23Z Sumner 5143 /* Cross Roads Expansion */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == With this expansion, each station (except the Inn) gives travelers a new option. There are also new travelers added. Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 7b8a2e7ec0781136dbefc471f49d6c37bdf99f67 1093 1089 2014-03-15T06:26:38Z Diversionarchitect 5221 /* Cross Roads Expansion */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == With this expansion, each station (except the Inn) gives travelers a new option. There are also new travelers added. Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf Note: The faces for the fortune die (gambling at the farm) has the faces: x0,x1,x2,x3,x3,x4 == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 0bddf507512bf0e338c2781499f2463416bbf225 1094 1093 2014-03-15T06:27:28Z Diversionarchitect 5221 /* Cross Roads Expansion */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == With this expansion, each station (except the Inn) gives travelers a new option. There are also new travelers added. Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf Note: The faces for the Fortune die (for gambling at the farm) has these faces: x0,x1,x2,x3,x3,x4 == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 1d23a61bd5a434ef3902015e87c1783b487f2f36 1095 1094 2014-03-15T06:27:48Z Diversionarchitect 5221 /* Cross Roads Expansion */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == With this expansion, each station (except the Inn) gives travelers a new option. There are also new travelers added. Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf Note: The Fortune die (for gambling at the farm) has these faces: x0,x1,x2,x3,x3,x4 == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 886bfc75a19df7fd10826b09f158d27524676626 1096 1095 2014-03-15T06:28:05Z Diversionarchitect 5221 /* Cross Roads Expansion */ wikitext text/x-wiki == Object of the game == The players are travelers in Japan. They will follow the prestigious Tokaido and try to make this journey as rich an experience as possible. To do this, they will pass through magnificent countryside, taste delicious culinary specialties, purchase souvenirs, benefit from the virtues of hot springs, and have unforgettable encounters. ==Playing the game== The player whose Traveler is farthest behind on the road is the player who takes the next turn. This player must move his Traveler forward to the open space of his choice, freely passing over one or more open spaces. Once he has moved his Traveler, the player receives the benefit corresponding to this type of space. '''NOTE:''' Some of the spaces on the board are doubled. Double spaces are used only in games with 4 or 5 players. ==Description of the spaces== '''Farm''' : The player earns 3 coins. '''Hot Spring''' : The player takes a Hot Spring card. These cards are worth 2 or 3 points. '''Temple''' : The player must donate 1, 2, or 3 coins to the temple and scores 1 point for each donated coin. '''Encounters''' : The player draws one Encounter card and receives a bonus: :* Shokunin - one Souvenir :* Annaibito - one Panorama :* Samurai - 3 points :* Kuge - 3 coins :* Miko - 1 point and 1 coin to the Temple (from the bank) '''Village''' : The player draws 3 Souvenir cards and can purchase one or more of these cards. : Each Souvenir belongs to one of 4 types: small objects, clothing, art, and food & drinks. : Souvenirs cost 1, 2 or 3 coins. They are worth 1, 3, 5 or 7 points depending on the actual Souvenirs in a player’s collection. : As you purchase Souvenir cards, group them into sets; each set can contain only one Souvenir of each type. :* The first Souvenir in a set is worth 1 point. :* The second Souvenir in a set is worth 3 points. :* The third Souvenir in a set is worth 5 points. :* The fourth Souvenir in a set is worth 7 points. '''Panorama''' : Panoramas are made of 3, 4 or 5 sections. : When a player stops on a Panorama Station, if he doesn’t yet have any Panorama cards of this type, he takes a Panorama card of value « 1 ». : Otherwise, he takes the next number in ascending order and scores a number of points equal to the value of the card. : '''NOTE:''' Each traveler can create only a single panorama of each type; a traveler who completes a panorama can no longer stop on the spaces corresponding to that type (Sea, Mountain, or Paddy). '''Inns''' : All Travelers must stop at each Inn. : The Inns are the places where players can buy Meal cards. Meal cards cost 1, 2 or 3 coins and all give 6 victory points. : The first traveler occupies the space nearest the road, and later travelers form a line after him. : When the first traveler arrives at an Inn, he draws as many Meal cards as there are players, plus 1. : He can then purchase one Meal card of his choice by paying its price. : He adds this card, face up, to his collection and places the remaining cards next to the board, face down. : He then must wait for the other travelers to arrive at the Inn. Upon arrival, each traveler can purchase one of the remaining Meal cards. : '''IMPORTANT:''' :* A traveler cannot taste the same culinary specialty twice during his journey. :* A traveler can never purchase more than one Meal card per Inn. :* A traveler is never obliged to purchase a Meal card. == End of the journey == When all of the Travelers have arrived in Edo at the last Inn, the game ends. The travelers score additional points depending on their ranking as donors to the Temple: * The most generous donor scores 10 points. * The second scores 7 points. * The third scores 4 points. * All other donors score 2 points. Award the following achievement cards to the appropriate travelers. Each achievement card is worth 3 points. If players are tied for an achievement, then they each receive the 3 points. * Gourmet - the player with the highest total face value of Meal cards. * Collector - the player with the most Souvenir cards. * Bather - the player with the most Hot Spring cards. * Chatterbox - the player with the most Encounter cards. Note: There are 3 other achievement cards that are awarded during game play. One for each different type of panorama and each is awarded to the player that is first to complete the specific panorama. Note: If players are tied after the final scoring, then the player with the most achievement cards wins. ---- '''(Extracted from the English rule book)''' = Expansions = == Cross Roads Expansion == With this expansion, each station (except the Inn) gives travelers a new option. There are also new travelers added. Rules for playing the Cross Roads Expansion can be found here: http://www.funforge.fr/US/files/crossroads/Crossroads_Rules_US.pdf Note: The Fortune die (for gambling at the farm) has these faces: x0, x1, x2, x3, x3, x4 == The New Encounters == This expansion adds 4 new Encounter cards. Description of the cards is here: http://boardgamegeek.com/image/1452161/tokaido-the-new-encounters?size=large 1c97478bd1c3715be28d901489239d5133e5bc2a Gamehelptroyes 0 26 1092 708 2014-03-14T22:38:05Z Drstuey 3099 + add the different levels of scoring for the characters wikitext text/x-wiki == Goal == Be the player with the most victory points at the end of the game. == Rules summary == (This rules summary is based on the game help written by [http://boardgamegeek.com/user/pregremlin Andrew Agard] for Board Game Geek, under Creative Commons license. Please see [http://boardgamegeek.com/filepage/66976/complete-rules-reference-1-page-fold-in-half original file] here for details. Thank you Andrew !) ===Initial Placement=== * In clockwise order place a citizen in empty space of one building * Continue in counter clockwise order from last player and so on until all citizens placed ===Game play=== ====Phase 0: Reveal the Activity cards==== Reveal activity card for each color corresponding to current round (first 3 rounds only) ====Phase 1: Income and salaries==== Receive 10 deniers and pay 1 per Bishopric and 2 per Palace citizen or lose 2 VP ====Phase 2: Assembling the workforce==== Roll yellow/white/red die per citizen in City Hall/Bishopric/Palace and place in district ====Phase 3: Events==== Reveal top red and white or yellow Event and place to right of queue (unlimited Events) Events take effect from left to right * Military: start player takes 1 black die per die symbol * Other events: see annex (can’t execute: execute as much as possible and lose 2 VP) After Events, roll black dice and counter: * Start player must counter highest-value with one or more dice form district. Total value must be >= die. Discard dice. May counter several dice at once * Gain 1 influence per dice countered (can’t counter: discard die and lose 2 VP) * In order players must counter highest remaining until all countered * Use any color dice. Double red dice. Can’t buy dice or use activity cubes ====Phase 4: Actions==== See below. ====Phase 5: End of the round==== Receive deniers from district Return citizens lying on buildings to personal supplies Return unused dice to general supply Pass start player card left. ===Actions=== In order each player can use 1-3 matching dice for one action or pass. Round ends when no dice available or all players have passed May pay player or bank (gray) to use dice. If using 1/2/3 dice pay 2/4/6 deniers per dice ====Activate Activity Card==== Must have tradesman. Hire if necessary by paying indicated amount in deniers. Place citizen on free space (or illustration if full) from personal supply or any board location * 1 tradesman per player per card * Must activate at least once if tradesman hired * Citizens on card cannot be moved to newly freed space Immediate effect: activation cost (round down) determines color and use of dice Delayed effect: place cubes equal to activation cost. Use later (one cube per action) ====Construct Cathedral==== * Use 1-3 white dice to place 1-3 cubes on same-numbered construction site * Must place on lower levels before placing in higher levels for each set of valued spaces * Gain 1 VP and 1 or 2 Influence for each cube placed in spaces 1-3 or 4-6 ====Combat Events==== * Activation cost (round down) defines dice and number of cubes to place on card * Place cubes on small banner starting with upper left. Gain 1 influence for each. Only place on a single card each action and can’t place more cubes than banners Event countered when banners filled * Most cubes earns higher reward (tied: total rounded down, 2nd earns nothing). If only 1 player on card earn both rewards. * 2nd most cubes earns smaller reward (tied: total rounded down) * Most cubes takes card (tied: player who placed first). Discard if neutral has most * Marauding: rewards given, cubes removed, and then event is available again ====Place a citizen on building==== * Use exactly one die to place one citizen from personal supply or any board location on first space of matching building row or space corresponding to die color and value * Shift existing citizens to right. Citizens pushed off are laid on building illustration * If already have expelled citizen nobody can expel your citizens from that building ====Use Agriculture==== * Gain number of deniers equal to total value divided by 2 (round down) ===Pass=== * If dice still in any city square, pass and receive 2 deniers which are placed in district * Each turn add another denier to district ==Characters scoring== ===Chretien de Troyes=== Awards citizens placed on the 3 principal buildings * 1 VP for 3+ (4+ with 2 players) * 3 VP for 5+ (6+ with 2 players) * 6 VP for 7+ (8+ with 2 players) ===Urbain IV=== Awards cubes placed on the cathedral * 1 VP for 3+ (4+ with 2 players) * 3 VP for 5+ (6+ with 2 players) * 6 VP for 7+ (8+ with 2 players) ===Thibaut II=== Awards deniers * 1 VP for 6+ * 3 VP for 12+ * 6 VP for 18+ ===Hugues de Payns=== Awards influence * 1 VP for 5+ * 3 VP for 10+ * 6 VP for 15+ ===Le Florentin=== Awards tradesman on the activity cards * 1 VP for 2+ * 3 VP for 4+ * 6 VP for 6+ ===Henry I=== Awards event cards * 1 VP for 1+ * 3 VP for 3+ * 6 VP for 5+ e000835f92220c6f16d0a1a52e246e365c2f0017 Gamehelptzolkin 0 129 1098 978 2014-03-23T02:55:55Z Sumner 5143 /* For Each Round */ wikitext text/x-wiki Rules are here: http://czechgames.com/files/Tzolkin_EN.pdf == '''For Each Round''' == - Each player takes their turn, going around the table - On a food day, feed your workers and take your rewards - Advance the Calendar == '''Player turn''' == - '''Beg for corn:''' If you have 2 or less corn, you may beg for corn unless you are at the bottom step of all three temples. (Turn in all corn, take 3 corn and move one step down on any temple) - '''Either place workers OR pick up workers''' -- ''Placing workers'': pay for the number of workers you place plus the cost noted next to each placed workers -- ''Picking up Workers'': You may pick up one or more of your placed workers. For each worker picked up either --- Use the action where your worker was standing --- Use a lower action on the wheel from where your worker was standing(if not standing on a free action space,pay 1 corn for each step back) --- Do nothing. == '''Food Day''' == -'''Feed the Workers''' Each players must pay 2 corn for each worker they have in play (on a gear or in front of you) with the exception for any farms you own. You must feed as many workers as you have corn for. -- For each worker not fed, you lose 3 points -'''Take Rewards''' --''Middle of Age (Brown days)'' --- Receive items shown on the left edge of temples from step you are on and all steps below -- ''End of Age (Teal Days)'' --- Remove all Age 1 buildings and fill spaces with Age 2 buildings/ --- Receive points shown on the right edge of temples for the step you are on. --- Bonus points for each temple for person with the highest market. Bonus amount is shown above each temple. For ties, everyone receives half the bonus. == '''Advance the Calendar''' == If there is no worker on the starting player space, place a corn on the current Tzolk'in gear tooth and advance the gear one day. Otherwise: - The worker on the Starting Player space returns to the player -- If this person was not the starting player, they take the starting player marker. -- If they were the starting player, the starting player marker is moved to the player on his left - Advance the Tzolk'in Gear one day. - The player that placed their worker on the Starting Player space may decide to advance the gear one extra day if the player board is light side up AND this advance will not push a player off the gears. -- If you use this option, flip your board to dark side up. -- This does not avoid food days. df8f36fa1da14ce79c2df6f252a145990f5f28d2 Gamehelpkalah 0 162 1100 2014-04-08T09:29:08Z Stst 2180 Created page with "===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called kalah. A player owns the six holes cl..." wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called kalah. A player owns the six holes closest to him and the kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has captured most stones is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== "Pie rule" - Second player can take opponent's first move and change side of the board. "Empty Capture" - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' de34e78cae9854089d843dd9b32e6e461460ee89 1101 1100 2014-04-09T06:46:15Z Stst 2180 wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== "Pie rule" - Second player can take opponent's first move and change side of the board. "Empty Capture" - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' dd278ce076daa8be794993744bdaaebc59f8a14a Gamehelpkalah 0 162 1102 1101 2014-04-10T04:26:55Z Stst 2180 /* RULES */ wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== "Pie rule" - Second player can take opponent's first move and change side of the board. "Empty Capture" - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' 8beba5bfc392f48ad6ec7a1b1646ae5833f1172a 1103 1102 2014-04-10T04:27:44Z Stst 2180 /* RULES */ wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== "Pie rule" - Second player can take opponent's first move and change side of the board. "Empty Capture" - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' dd278ce076daa8be794993744bdaaebc59f8a14a 1104 1103 2014-04-10T07:48:00Z Stst 2180 wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== "Pie rule" - Second player can take opponent's first move and change side of the board. "Empty Capture" - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' 8beba5bfc392f48ad6ec7a1b1646ae5833f1172a 1105 1104 2014-04-10T09:40:17Z Stst 2180 /* VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME) */ wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== «Pie rule» - Second player can take opponent's first move and change side of the board. «Empty Capture» - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' 4fe7f391761f5d53938f87ca7d6a78c95d1acc89 1112 1105 2014-04-28T20:02:18Z Stst 2180 /* VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME) */ wikitext text/x-wiki ===EQUIPMENT=== Kalah is played on a board of two rows, each consisting of six round holes that have a large store at either end called Kalah. A player owns the six holes closest to him and the Kalah on his right side. Beginners may start with three stones in each hole, but the game becomes more and more challenging by starting with 4, 5 or up to 6 stones in each hole. ===OBJECT=== The object of the game is to capture more stones than one's opponent. ===RULES=== At the beginning of the game, from 3 to 6 stones are placed in each hole. Play is counterclockwise. On a turn, the player removes all stones from one of the holes under his/her control. Moving counter-clockwise, the player drops one stone in each hole in turn, including the player's own Kalah but not his/her opponent's Kalah. If the last stone is dropped into an opponent's hole or a non-empty hole of the player, the move ends without anything being captured. If the last stone falls into the player's Kalah, he must move again. If the last stone is put into an empty hole owned by the player, he captures all stones of the opposite hole together with the capturing stone and puts them in his/her Kalah. If the opposite hole is empty, nothing is captured. A capture ends the move. ===END OF THE GAME=== The game ends: When a player, at his turn, is unable to move, because his no longer has any stones in any of his holes. The remaining stones are captured by his opponent. OR When a player collected more half of all stones in his/her Kalah. The player who has collected most stones in his/her Kalah is declared the winner. ===VARIATIONS (OPTION OF THIS ADAPTATION OF THE GAME)=== «Pie rule» - Second player can take opponent's first move and change sides of the board. «Empty Capture» - This variant permits to capture the last stone when landing in an empty hole on the player's own side even when the opposite hole of the opponent is empty. ===COMMENT FROM THE DEVELOPER (stst)=== '''THESE RULES ARE USED AT THIS ADAPTATION OF THE GAME''' 2f516a35fa025af19ded3b509148e680b150ee45 Steps to create a BGA game 0 117 1106 957 2014-04-19T21:57:26Z Adam badura 5433 Missing character added wikitext text/x-wiki Here's a summary of the different steps you would follow when developing a game with BGA Studio. {| class="wikitable" |- ! Step !! How to reach this ste?p !! What happened during the step? |- | Initial || [[How to join BGA developer team?]] || You can choose to join an existing team / create a new project |- | Assigned || You choosed a game || You can start the development of the game |- | Pre-alpha || You've started to write some piece of code || You develop the game. During this phase, we can assist you with the framework and give you some pieces of advice. |- | Alpha || You tell us that your development is finished || "BGA review": we are reviewing your game and check if it respects [http://fr.slideshare.net/boardgamearena/bga-studio-guidelines BGA guidelines]. If not, we will ask you (and help you) to fix them. |- | Private beta || We give a "go" || "Publisher review": On preproduction platform, the publisher, the designer, we and you can test the game together and separately. We help you to take into account remarks from the publisher and the designer. |- | Public beta || The adaptation is approved by the publisher || We find together a good launch date for the game, we announce the game on BGA news, and then player can start to play! During the first days, it is common that some bugs are reported by players, and you can fix them following the instructions in [[Post-release phase]]. |- | Gold || The game is stable on BGA || Congrats! You can still modify and optimize things following the instructions in [[Post-release phase]]. |} 09800b383fedaa37ef8609c93cd105d61a633472 Gamehelplibertalia 0 105 1107 756 2014-04-24T15:09:05Z Lowre 5460 /* How to Play */ Come giocare wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. == '''How to Play''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Tokens are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. Come giocare Il gioco consiste in tre campagne, suddivise in 6 giorni. Ogni giorno ha 4 fasi : Fase 1 : Sunrise Fase 2 : Day Fase 3 : Dusk Fase 4 : Notte Nella prima campagna , tutti i giocatori hanno un insieme casuale identico di 9 carte di equipaggio e 10 dobloni . Fase 1 : Sunrise In questa fase , ogni giocatore sceglie segretamente una carta equipaggio da giocare . Una volta che tutti sono pronti , i giocatori rivelano i loro membri dell'equipaggio scelti e metterli sulla nave cargo dal basso a sinistra per il più alto sulla destra . Se c'è un pareggio , il numero tie breaker inferiore va sinistra superiore. Durante questa fase , tutte le azioni speciali che si svolgono durante l'alba capita da sinistra a destra . Fase 2 : Giornata L'unica cosa che accade in questa fase è tutti i membri dell'equipaggio ad azione giorno devono prendere , in ordine crescente di rango . (più basso va prima) . Fase 3 : Dusk Ogni equipaggio deve richiedere un token bottino da sotto la nave . I token sono scelti in ordine di rango ( l'opposto di Fase 2) diminuendo così l'equipaggio più alto rango ottenere la migliore selezione dei gettoni . Dopo un equipaggio rivendica un bottino e si risolve con la loro azione crepuscolo ( se presenti ) siano immediatamente messi in tana del loro giocatore prima della prossima bottino è affermato ( che è importante se qualcuno sta sostenendo una sciabola in seguito . ) In alcuni casi ci potrebbe essere più bottino di rivendicare quando il turno di un equipaggio di credito viene risolto il che significa che non ottengono bottino e andare al loro covo a mani vuote . Fase 4 : Notte L'unica cosa che accade in questa fase è membri dell'equipaggio usano le loro azioni notturne . La differenza è tutti i membri dell'equipaggio nella tua tana risolvere le loro azioni notturne ogni notte . In modo che Barkeep ( la sua azione è di segnare voi 1 doblone ogni notte ) si gioca il giorno 1 della campagna in corso , vi terrà pagare ogni notte ( finché egli rimane in vita . ) Giocare le carte la sera presto ! Nota : Tutto l'equipaggio deve risolvere il più delle loro azioni possibili, anche se sfavorevole a se stessi o il loro controllo ! Il Brute potrebbe uccidere se stesso , il Cannoneer deve pagare 3 oro , anche se non ci sono equipaggi ammissibili in una tana per uccidere , il commerciante venderà il set di gioielli o mappe del tesoro se non si hanno altre serie di tesori di corrispondenza. Dopo il 6 ° giorno della campagna , il round è finito . I giocatori risolvono tutti " fine della campagna" azioni dei membri dell'equipaggio ancora vivo nelle loro tane (sì , i membri dell'equipaggio possono essere uccisi ) . Tutti i membri dell'equipaggio utilizzati vengono rimossi dal gioco . Ogni giocatore somma il valore dei loro gettoni bottino e dobloni ( restituirli alla banca ) e sposta il proprio segnalino punteggio più avanti sulla pista . Ogni giocatore deve avere tre carte rimaste in mano . Questi riporto nei prossimi 2 campagne . Per impostazione per la prossima campagna ogni giocatore riceve 10 nuovi dobloni e aggiungere un insieme casuale identico di 6 carte equipaggio a tre membri dell'equipaggio rimasto . Così i giocatori possono avere una mano leggermente diversi l'uno dall'altro andando avanti . Turni 2 e 3 il gioco proprio come turno 1 . Alla fine della partita il giocatore con il maggior numero di punti vittoria è il vincitore . c133f13c9b4c50f66a04f77499b6535010ffac1d 1108 1107 2014-04-24T15:10:39Z Lowre 5460 wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. == '''How to Play''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Tokens are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. '''Come giocare''' Il gioco consiste in tre campagne, suddivise in 6 giorni. Ogni giorno ha 4 fasi : Fase 1 : Sunrise Fase 2 : Day Fase 3 : Dusk Fase 4 : Notte Nella prima campagna , tutti i giocatori hanno un insieme casuale identico di 9 carte di equipaggio e 10 dobloni . Fase 1 : Sunrise In questa fase , ogni giocatore sceglie segretamente una carta equipaggio da giocare . Una volta che tutti sono pronti , i giocatori rivelano i loro membri dell'equipaggio scelti e metterli sulla nave cargo dal basso a sinistra per il più alto sulla destra . Se c'è un pareggio , il numero tie breaker inferiore va sinistra superiore. Durante questa fase , tutte le azioni speciali che si svolgono durante l'alba capita da sinistra a destra . Fase 2 : Giornata L'unica cosa che accade in questa fase è tutti i membri dell'equipaggio ad azione giorno devono prendere , in ordine crescente di rango . (più basso va prima) . Fase 3 : Dusk Ogni equipaggio deve richiedere un token bottino da sotto la nave . I token sono scelti in ordine di rango ( l'opposto di Fase 2) diminuendo così l'equipaggio più alto rango ottenere la migliore selezione dei gettoni . Dopo un equipaggio rivendica un bottino e si risolve con la loro azione crepuscolo ( se presenti ) siano immediatamente messi in tana del loro giocatore prima della prossima bottino è affermato ( che è importante se qualcuno sta sostenendo una sciabola in seguito . ) In alcuni casi ci potrebbe essere più bottino di rivendicare quando il turno di un equipaggio di credito viene risolto il che significa che non ottengono bottino e andare al loro covo a mani vuote . Fase 4 : Notte L'unica cosa che accade in questa fase è membri dell'equipaggio usano le loro azioni notturne . La differenza è tutti i membri dell'equipaggio nella tua tana risolvere le loro azioni notturne ogni notte . In modo che Barkeep ( la sua azione è di segnare voi 1 doblone ogni notte ) si gioca il giorno 1 della campagna in corso , vi terrà pagare ogni notte ( finché egli rimane in vita . ) Giocare le carte la sera presto ! Nota : Tutto l'equipaggio deve risolvere il più delle loro azioni possibili, anche se sfavorevole a se stessi o il loro controllo ! Il Brute potrebbe uccidere se stesso , il Cannoneer deve pagare 3 oro , anche se non ci sono equipaggi ammissibili in una tana per uccidere , il commerciante venderà il set di gioielli o mappe del tesoro se non si hanno altre serie di tesori di corrispondenza. Dopo il 6 ° giorno della campagna , il round è finito . I giocatori risolvono tutti " fine della campagna" azioni dei membri dell'equipaggio ancora vivo nelle loro tane (sì , i membri dell'equipaggio possono essere uccisi ) . Tutti i membri dell'equipaggio utilizzati vengono rimossi dal gioco . Ogni giocatore somma il valore dei loro gettoni bottino e dobloni ( restituirli alla banca ) e sposta il proprio segnalino punteggio più avanti sulla pista . Ogni giocatore deve avere tre carte rimaste in mano . Questi riporto nei prossimi 2 campagne . Per impostazione per la prossima campagna ogni giocatore riceve 10 nuovi dobloni e aggiungere un insieme casuale identico di 6 carte equipaggio a tre membri dell'equipaggio rimasto . Così i giocatori possono avere una mano leggermente diversi l'uno dall'altro andando avanti . Turni 2 e 3 il gioco proprio come turno 1 . Alla fine della partita il giocatore con il maggior numero di punti vittoria è il vincitore . f4df15211fe2334967c8177c2019865512b8e6b5 1109 1108 2014-04-24T15:15:28Z Lowre 5460 wikitext text/x-wiki Libertalia is an amazing and original card game. During three campaigns, you have to gather doubloons and booty tiles to become the wealthiest pirate. Each turn, you play one card to try to be the first to choose which share of the booty will be yours. Of course, each card has special powers that completely break this "routine". One thing that makes Libertalia original is that everyone starts with the same set of cards. That's why you'll have to choose carefully which moment to play each card, and to try to read your opponents' strategies. == '''How to Play''' == The game consists of 3 campaigns, broken up into 6 days. '''Each day has 4 phases: Phase 1: Sunrise Phase 2: Day Phase 3: Dusk Phase 4: Night''' In the first campaign, all players have an identical random set of 9 crew cards and 10 doubloons. '''''Phase 1: Sunrise''''' In this phase, each player secretly chooses one crew card to play. Once everyone is ready, the players reveal their chosen crew members and place them on the cargo ship from lowest on the left to the highest on the right. If there is a tie, the lower tie breaker number goes the left of the higher. During this phase, all special actions that take place during sunrise happen from left to right. '''''Phase 2: Day''''' The only thing that happens in this phase is all crew members with a day action must take it, in increasing order of rank. (lowest goes first). '''''Phase 3: Dusk''''' Each crew must claim a booty token from below the ship. Tokens are chosen in decreasing order of rank (the opposite of Phase 2) so the highest ranking crew get the best selection of tokens. After a crew claims a booty and resolves their dusk action (if any) they are immediately placed in their player's den before the next booty is claimed (which is important if someone is claiming a sabre later.) In some circumstances there might be no more booty to claim when a crew's turn to claim is resolved which means they get no booty and go to their den empty handed. '''''Phase 4: Night''''' The only thing that happens in this phase is crew members use their Night actions. The difference is all crew members in your den resolve their night actions every night. So that Barkeep (his action is to score you 1 doubloon each night) you played on day 1 of the current campaign, will keep paying you every night (as long as he stays alive.) Play those night cards early! Note: All crew must resolve as much of their actions as possible, even if disadvantageous to themselves or their controller! The Brute might kill himself, the Cannoneer has to pay 3 gold even if there are no eligible crew in a den to kill, the Merchant will sell your set of jewels or treasure maps if you have no other sets of matching treasure. After the 6th day of the campaign, the round is over. Players resolve all “end of campaign” actions of crew members still alive in their dens (yes, crew members can be killed). All used crew members are removed from the game. Each player adds up the value of their booty tokens and doubloons (returning them to the bank) and moves their score marker ahead on the track. Each player should have 3 cards left in their hand. These carry over into the next 2 campaigns. To setup for the next campaign each player gets 10 new doubloons and add an identical random set of 6 crew cards to the three crew left over. Thus players may have a slightly different hand from each other going forward. Rounds 2 and 3 play just like round 1. At the end of the game the player with the most victory points is the winner. 1db47195e1202cd4d33fe0ee8d989c0b20b42c52 Gamehelpeightmastersrevenge 0 163 1110 2014-04-26T01:07:10Z Jinnh 4158 Created page with "Overview and aim of the game 8 Masters’ Revenge is a fighting game for 1 to 4 players (only available for 2 players on BGA). You are a martial arts master and you want to ..." wikitext text/x-wiki Overview and aim of the game 8 Masters’ Revenge is a fighting game for 1 to 4 players (only available for 2 players on BGA). You are a martial arts master and you want to beat your opponents, strike after strike. Your only weapons are your 2 hands, represented by the 2 cards you have in play. Find the weak point in your opponent’s guard to deal damage to him. As soon as his ‘’health‘’ marker reaches the last space of his life track (whose length depends of the number of players), he is out of the game. The last surviving player wins the fight. d22dc9606ab12588626e4484a0a5d397f5804020 1111 1110 2014-04-26T01:15:45Z Jinnh 4158 wikitext text/x-wiki Overview and aim of the game 8 Masters’ Revenge is a fighting game for 1 to 4 players (only available for 2 players on BGA). You are a martial arts master and you want to beat your opponents, strike after strike. Your only weapons are your 2 hands, represented by the 2 cards you have in play. Find the weak point in your opponent’s guard to deal damage to him. As soon as his "health" marker reaches the last space of his life track (whose length depends of the number of players), he is out of the game. The last surviving player wins the fight. a7b9a9eb420c0ef12ced22819d203e4a3fded472 Stock 0 97 1113 1082 2014-04-30T03:45:33Z Pikiou 1872 /* Complete stock component reference */ wikitext text/x-wiki "Stock" is a javascript component that you can use on your game interface to display a set of elements of the same size that need to be arranged in one or several lines. Stock is very flexible and is the most used component in BGA games. Stock is used for example: * To display set of cards, typically hands (ex: in Hearts, Seasons, The Boss, Race for the Galaxy, ...). * To display items in player panels (ex: Takenoko, Amyitis, ...) * ... in many other situations. For example, black dice and cubes on cards in Troyes are displayed with stock components. Using stock: * Your items are arranged nicely and sorted by type. * When adding (or removing) items to the set. All items slide smoothly to their new position in the set to host the new one. * Select/unselect items is a built-in functionnality. * You don't have to care about inserting/removing HTML piece of code: the entire life of the stock is managed by the component. == Using stock: a simple example == Let's have a look on how the stock is used in game "Hearts" to display a hand of standard cards. At first, don't forget to add "ebg/stock" as a dependency: <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <==== HERE ], </pre> The stock is initialized in Javascript "setup" method like this: <pre> // Player hand this.playerHand = new ebg.stock(); this.playerHand.create( this, $('myhand'), this.cardwidth, this.cardheight ); </pre> Explanations: * We create a new stock object for the player hand. * As parameters of the "create" method, we provide the width/height of an item (=a card), and the container div "myhand" - which is a simple void "div" element defines in our HTML template (.tpl). Then, we must tell the stock what are the items it is going to display during its life: the 52 cards of a standard card game. Of course, we did not create 52 different images, but create a "CSS sprite" image named "cards.jpg" with all the cards arranged in 4 rows and 13 columns. Here's how we tell stock what are the items type to display: <pre> // Explain there are 13 images per row in the CSS sprite image this.playerHand.image_items_per_row = 13; // Create cards types: for( var color=1;color<=4;color++ ) { for( var value=2;value<=14;value++ ) { // Build card type id var card_type_id = this.getCardUniqueId( color, value ); this.playerHand.addItemType( card_type_id, card_type_id, g_gamethemeurl+'img/cards.jpg', card_type_id ); } } </pre> Explanations: * At first, we tell the stock component that our CSS sprite contains 13 items per row. This way, it can find the correct image for each card type id. * Then for the 4x13 cards, we call "addItemType" method that create the type. The arguments are the type id, the weight of the card (for sorting purpose), the URL of our CSS sprite, and the position of our card image in the CSS sprite. Note: in this specific example we need to generate a unique ID for each type of card based on its color and value. This is the only purpose of "getCardUniqueId". From now, if we need to add - for example - the 5 of Heart to player's hand, we can do this. this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ) ); In reality, cards have some IDs, which are useful to manipulate them. This is the reason we are using "addToStockWithId" instead: this.playerHand.addToStock( this.getCardUniqueId( 2 /* 2=hearts */, 5 ), my_card_id ); If afterwards we want to remove this card from the stock: this.playerHand.removeFromStockById( my_card_id ); == Complete stock component reference == '''create( page, container_div, item_width, item_height ):''' With create, you create a new stock component. Parameters: * page: the container page. Usually: "this". * container_div: the container "div" element (a void div element in your template, with an id). * a stock item width and height, in pixels. (See "Hearts" example above). '''count():''' Return the total number of items in the stock right now. '''addItemType( type, weight, image, image_position ):''' Define a new type of item to the stock. This is mandatory to define a new item type before adding it to the stock. Example: if you want to have a stock control that can contains cubes of 3 different colors, you must add 3 item types (one for each color). Parameters: * type: ID of the type to add. You can choose any positive integer. All item types must have distinct IDs. * weight: weight of items of this type. Weight value is used to sort items of the stock during the display. Note that you can specify the same weight for all items (in this case they are not sorted and their order might change randomly at any time). * image: URL of item image. Most of the time, you will use a CSS sprite for stock item, so you have to specify CSS sprite image here. Be careful: you must specify the image url as this: <pre> g_gamethemeurl+'img/yourimage.png' </pre> * image_position: if "image" specify the URL of a CSS sprite, you must specify the position of the item image in this CSS sprite. For example, if you have a CSS sprite with 3 cubes with a size of 20x20 pixels each (so your CSS image has for example a size of 20x60 or 60x20), you specify "0" for the first cube image, 1 for the second, 2 for the third. Important: there are more than one line of items in your CSS sprite, you must specify how many items per line you have in your CSS sprite like this: <pre> // Specify that there is 10 image items per row in images used in "myStockObject" control. this.myStockObject.image_items_per_row = 10; </pre> '''addToStock( type, from )''' Add an item to the stock, with the specified type. To make your life easy, in most of the case we suggest you to use "addToStockWithId" in order to give an ID to the item added. "addToStock" is perfect when you are using a stock controls with items that are generic game material that does not need to be addressed individually (ex: a bunch of money tokens). Parameters: * type: ID of the item type to use (as specified in "addItemType") * from: OPTIONNAL: if you specify a HTML item here, the item will appear on this item and will be slided to its position on the stock item. Example: <pre> // Add a money token to the "player money" stock. // The money token will appear on "player_id" player panel and will move to its position. this.playerMoney.addToStock( MONEY_TOKEN, 'overall_player_board_'+player_id ); </pre> Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''addToStockWithId( type, id, from )''' This is exactly the same method than "addToStock", except that you associate an ID to the newly created item. This is especially useful: * When you need to know which item(s) has been selected by the user (see "getSelectedItems"). * When you need to remove a specific item from the stock with "removeFromStockById" Important: for a given stock control, you must use either addToStock or addToStockWithId, but NEVER BOTH OF THEM. '''removeFromStock( type, to )''' Remove an item of the specific type from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeFromStockById( id, to )''' Remove an item with a specific ID from the stock. "to" is an optional parameter. If "to" contains the ID of an HTML element, the item removed from the stock is slided to this HTML element before it disappear. '''removeAll()''' Remove all items from the stock. '''getPresentTypeList()''' Return an array with all the types of items present in the stock right now. Example: <pre> this.myStockControl.removeAll(); this.myStockControl.addToStock( 65 ); this.myStockControl.addToStock( 34 ); this.myStockControl.addToStock( 89 ); this.myStockControl.addToStock( 65 ); // The following returns: { 34:1, 65:1, 89:1 } var item_types = this.myStockControl.getPresentTypeList(); </pre> '''resetItemsPosition()''' If you moved an item from the stock control manually (ex: after a drag'n'drop) and want to reset their position to their original ones, you can call this method. '''item_margin''' By default, there is a margin of 5px between the items of a stock. You can change the member variable "item_margin" to change this. Example: <pre> this.myStockControl.item_margin=5; </pre> '''changeItemsWeight( newWeights )''' With this method you can change dynamically the weight of the item types in a stock control. Items are immediately re-sorted with the new weight. Example: with a stock control that contains classic cards, you can order them by value or by color. Using changeItemsWeight you can switch from one sort method to another when a player request this. newWeights is an associative array: item type id => new weight. Example: <pre> // Item type 1 gets a new weight of 10, 2 a new weight of 20, 3 a new weight of 30. this.myStockControl.changeItemsWeight( { 1: 10, 2: 20, 3: 30 } ); </pre> '''setSelectionMode( mode )''' For each stock control, you can specify a selection mode: * 0: no item can be selected by the player. * 1: a maximum of one item can be selected by the player at the same time. * 2: several items can be selected by the player at the same time. '''setSelectionAppearance( type )''' For each stock control, you can specify a selection highlighting type: * 'border': there will be a red border around selected items (this is the default). The attribute 'apparenceBorderWidth' can be used to manage the width of the border (in pixels). * 'disappear': the selected item will fade out and disappear. This is useful when the selection has the effect of destroying the item. * 'class': there will be an extra 'stockitem_selecte' css class added to the element when it is selected (and removed when unselected). You can override this class in the css file for your game. By default this class definition is: <pre> .stockitem_selected { border: 2px solid red ! important; } </pre> If you want to override it for example to change the border color add this in your <game>.css file: <pre> .stockitem_selected { border: 2px solid orange ! important; } </pre> NB: the 'class' highlighting type has not yet been deployed on the studio - 24/02/2014. This warning will be removed after the next upgrade. '''isSelected( id )''' Return true/false wether the specified item id has been selected or not. '''selectItem( id )''' Select the specified item. '''unselectItem( id )''' Unselect the specified item. '''unselectAll()''' Unselect all items of the stock. '''onChangeSelection''' This callback method is called when the player select/unselect an item of the stock. You can connect this to one of your method like this: <pre> dojo.connect( this.myStockControl, 'onChangeSelection', this, 'onMyMethodToCall' ); (...) onMyMethodToCall: function( control_name ) { // This method is called when myStockControl selected items changed var items = this.myStockControl.getSelectedItems(); // (do something) }, </pre> Note: The "control_name" argument is the ID (the "DOM" id) of the "div" container of your stock control. Using "control_name", you can use the same callback method for different Stock control and see which one trigger the method. '''getSelectedItems()''' Return the list of selected items, as an array with the following format: <pre> [ { type:1, id: 1001 }, { type:1, id: 1002 }, { type:3, id: 1003 } ... ] </pre> '''getUnselectedItems()''' Same as the previous one, but return unselected item instead of seleted ones. '''getAllItems()''' Get all items (same format than getSelectedItems and getUnselectedItems). '''setOverlap( horizontal_percent, vertical_percent )''' Make items on the stock control "overlap" on each other, to save space. By default, horizontal_overlap and vertical_overlap are 0. When horizontal_overlap=20, it means that a stock item must overlap on 20% of the width of the previous item. horizontal_overlap can't be over 100. vertical_overlap works differently: one items on two are shifted up. See "Jaipur" game to see an example to use of this function. '''onItemCreate''' Using onItemCreate, you can trigger a method each time a new item is added to the Stock, in order you can customize it. Complete example: <pre> // During "setup" phase, we associate our method "setupNewCard" with the creation of a new stock item: this.myStockItem.onItemCreate = dojo.hitch( this, 'setupNewCard' ); (...) // And here is our "setupNewCard": setupNewCard: function( card_div, card_type_id, card_id ) { // Add a special tooltip on the card: this.addTooltip( card_div.id, _("Some nice tooltip for this item"), '' ); // Note that "card_type_id" contains the type of the item, so you can do special actions depending on the item type // Add some custom HTML content INSIDE the Stock item: dojo.place( this.format_block( 'jstpl_my_card_content', { .... } ), card_div.id ); } </pre> == Tips when adding/removing items to/from Stock components == The usual way is the following: '''Situation A''': When you add a card to a stock item, and this card is '''not''' coming from another stock: use "addToStockWithId" with a "from" argument set to the element of your interface where card should come from. '''Situation B''': When you add a card to a stock item, and this card is coming from another stock: * on the destination Stock, use "addToStockWithId" with a "from" equals to the HTML id of the corresponding item in the source Stock. For example, If the source stock id is "myHand", then the HTML id of card 48 is "myHand_item_48". * then, remove the source item with "removeFromStockById". (note that it's important to do things in this order, because source item must still exists when you use it as the origin of the slide). '''Situation C''': When you move a card from a stock item to something that is not a stock item: * insert the card as a classic HTML template (dojo.place / this.format_block). * place it on the Stock item with "this.placeOnObject", using Stock item HTML id (see above). * slide it to its new position with "this.slideToObject" * remove the card from the Stock item with "removeFromStockById". Using the methods above, your cards should slide to, from and between your Stock controls smoothly 2b1ec2b323e69ad70dab9bdbb1cda8408ab3a751 Game interface logic: yourgamename.js 0 88 1114 1099 2014-04-30T23:36:24Z Quinarbre 4507 /* Tooltips */ wikitext text/x-wiki This is the main file for your game interface. Here you will define: * which actions on the page will generate calls to the server * what happens when you get a notification for change from the server and how it will show in the browser. == File structure == The details on how the file is structured is described directly with comments on the code skeleton provided to you. Basically, here's this structure: * constructor: here you can define variable global to your whole interface. * setup: this method is called when the page is refreshed, in order you can setup the game interface. * onEnteringState: the method is called when entering in a new game state. This way you can customize the view for this game state. * onLeavingState: the method is called when leaving a game state. * onUpdateActionButtons: called when entering in a new state, in order you can add action buttons in status bar. * (utility methods): at this place you can define your utility methods * (player's actions): at this place you can write your handlers for player's action on the interface (ex: click on an item). * setupNotifications: in this method you associate notifications with notification handlers. This way, for each game notification, you trigger a javascript method to handle it and update the game interface. * (notification handlers): at this place you can define your notifications handlers. == General tips == ; this.player_id : Id of the player on whose browser the code is running. ; this.isSpectator : Flag set to true if the user at the table is a spectator (not a player). : Note: if you want to hide some element for spectators, you'd better use [[Game_interface_stylesheet:_yourgamename.css#spectatorMode|CSS 'spectatorMode' class]]. ; this.gamedatas : Contains your initial set of datas to init the game, created at game start or game refresh (F5) : You can update it as needed to keep an up to date reference of the game on the client side if you need it (most of the time you don't). ; isCurrentPlayerActive() : Returns true if the player on whose browser the code is running is currently active (it's his turn to play) ; this.getActivePlayerId() : Return the ID of active player, or null if we are not in a "activeplayer" type state. ; this.getActivePlayers() : Return an array with the IDs of players that are currently active (or an empty array if there is not). == Dojo framework == BGA is using the [http://dojotoolkit.org/ Dojo Javascript framework]. The Dojo framework allows us to do complex things easier, and the BGA framework is using Dojo framework a lot. To realize game although, you only need to use a few part of the Dojo framework. All the Dojo methods you need to use are describe on this page. == Access and manipulate the DOM == '''$('some_html_element_id')''' The $() function is used to get some HTML element using its "id" attribute. Example 1: modify the content of a "span" element: <pre> In your HTML code: <span id="a_value_in_the_game_interface">1234</span> In your Javascript code: $('a_value_in_the_game_interface').innerHTML = "9999"; </pre> Note: $() is the standard method to access some HTML element with BGA Framework. You must not use "getElementById" function. '''dojo.style''' With dojo.style you can modify a CSS property of any HTML element of your interface. Examples: <pre> // Make an element disappear dojo.style( 'my_element', 'display', 'none' ); // Give an element a 2px border dojo.style( 'my_element', 'borderWidth', '2px' ); // Change the background position of an element // (very practical when you are using CSS sprite to transform an element to another) dojo.style( 'my_element', 'backgroundPosition', '-20px -50px' ); </pre> Note: you must always use dojo.style to modify CSS properties of HTML elements. Note²: if you have to modify several CSS properties of an element, or if you have some complex CSS transformation to do, you should consider using dojo.addClass/dojo.removeClass (see below). '''dojo CSS classes manipulation''' In many situation, a bunch of many small CSS property update can be replaced by a CSS class change (ie: you add a CSS class to your element instead of applying all modification manually). Advantages are: * All your CSS stuff remains in your CSS file. * You can add/remove a list of CSS modifications with a simple function and whithout error. * You can test if you applied the stuff to an element with "dojo.hasClass" method. Example from "Reversi": <pre> // We add "possibleMove" to an element dojo.addClass( 'square_'+x+'_'+y, 'possibleMove' ); // In our CSS file, the class is defined as: .possibleMove { background-color: white; opacity: 0.2; filter:alpha(opacity=20); /* For IE8 and earlier */ cursor: pointer; } // So we've applied 4 CSS property change in one line of code. // ... and when we need to check if a square is a possible move on client side: if( dojo.hasClass( 'square_'+x+'_'+y, 'possibleMove' ) ) { ... } // ... and if we want to remove all possible moves in one line of code (see "dojo.query" method): dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); </pre> Conclusion: we encourage you to use dojo.addClass, dojo.removeClass and dojo.hasClass to make your life easier :) '''dojo.query''' With dojo.query, you can query a bunch of HTML elements with a single function, with a "CSS selector" style. Example: <pre> // All elements with class "possibleMove": var elements = dojo.query( '.possibleMove' ); // Count number of tokens (ie: elements with class "token") on the board (ie: element with id "board"): dojo.query( '#board .token' ).length; </pre> But what is really cool with dojo.query is that you can combine it with almost all methods above. Examples: <pre> // Trigger a method when the mouse enter in any element with class "meeple": dojo.query( '.meeple' ).connect( 'onmouseenter', this, 'myMethodToTrigger' ); // Hide all meeples who are on the board dojo.query( '#board .meeple' ).style( 'display', 'none' ); </pre> '''dojo.place''' dojo.place is the best function to insert some HTML code somewhere in your game interface without breaking something. It is much better to use that "innerHTML=''" method as soon as you must insert HTML tags and not only values. <pre> // Insert your HTML code as a child of a container element dojo.place( "<your html code>", "your_container_element_id" ); // Replace the container element with your new html dojo.place( "<your html code>", "your_container_element_id", "replace" ); </pre> Note: the third parameter of dojo.place can take various interesting value: "first", "after", ... [http://dojotoolkit.org/reference-guide/1.7/dojo/place.html See full doc on dojo.place]. Usually, when you want to insert some piece of HTML in your game interface, you should use "[[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]". '''addStyleToClass: function( cssClassName, cssProperty, propertyValue )''' Same as dojo.style(), but for all the nodes set with the specified cssClassName === Animations === '''Dojo Animations''' BGA animations is based on Dojo Animation ([http://dojotoolkit.org/documentation/tutorials/1.8/animation/ see tutorial here]). However, most of the time, you can just use methods below, which are built on top of Dojo Animation. Note: one interesting method from Dojo that could be useful from time to time is "Dojo.Animation". It allows you to make any CSS property "slide" from one value to another. '''this.slideToObject( mobile_obj, target_obj, duration, delay )''' You can use slideToObject to "slide" an element to a target position. Sliding element on the game area is the recommended and the most used way to animate your game interface. Using slides allow players to figure out what is happening on the game, as if they were playing with the real boardgame. The parameters are: * mobile_obj: the ID of the object to move. This object must be "relative" or "absolute" positioned. * target_obj: the ID of the target object. This object must be "relative" or "absolute" positioned. Note that it is not mandatory that mobile_obj and target_obj have the same size. If their size are different, the system slides the center of mobile_obj to the center of target_obj. * duration: (optional) defines the duration in millisecond of the slide. The default is 500 milliseconds. * delay: (optional). If you defines a delay, the slide will start only after this delay. This is particularly useful when you want to slide several object from the same position to the same position: you can give a 0ms delay to the first object, a 100ms delay to the second one, a 200ms delay to the third one, ... this way they won't be superposed during the slide. BE CAREFUL: The method returns an dojo.fx animation, so you can combine it with other animation if you want to. It means that you have to call the "play()" method, otherwise the animation WON'T START. Example: <pre> this.slideToObject( "some_token", "some_place_on_board" ).play(); </pre> '''this.slideToObjectPos( mobile_obj, target_obj, target_x, target_y, duration, delay )''' This method does exactly the same than "slideToObjectPos", except than you can specify some (x,y) coordinates. This way, "mobile_obj" will slide to the specified x,y position relatively to "target_obj". Example: slide a token to some place on the board, 10 pixels to the bottom: <pre> this.slideToObjectPos( "some_token", "some_place_on_board", 0, 10 ).play(); </pre> '''this.slideTemporaryObject( mobile_obj_html, mobile_obj_parent, from, to, duration, delay )''' This method is useful when you want to slide a temporary HTML object from one place to another. As this object does not exists before the animation and won't remain after, it could be complex to create this object (with dojo.place), to place it at its origin (with placeOnObject) to slide it (with slideToObject) and to make it disappear at the end. slideTemporaryObject does all of this for you: * mobile_obj_html is a piece of HTML code that represent the object to slide. * mobile_obj_parent is the ID of an HTML element of your interface that will be the parent of this temporary HTML object. * from is the ID of the origin of the slide. * to is the ID of the target of the slide. * duration/delay works exactly like in "slideToObject" Example: <pre> this.slideTemporaryObject( '<div class="token_icon"></div>', 'tokens', 'my_origin_div', 'my_target_div' ); </pre> '''this.slideToObjectAndDestroy: function( node, to, time, delay )''' This method is a handy shortcut to slide an existing HTML object to some place then destroy it upon arrival. It can be used for example to move a victory token or a card from the board to the player panel to show that the player earns it, then destroy it when we don't need to keep it visible on the player panel. It works the same as this.slideToObject and takes the same arguments. Example: <pre> this.slideToObjectAndDestroy( "some_token", "some_place_on_board", 1000, 0 ); </pre> '''this.fadeOutAndDestroy( node )''' This function fade out the target HTML node, then destroy it. Example: <pre> this.fadeOutAndDestroy( "a_card_that_must_disappear" ); </pre> CAREFUL: the HTML node still exists until during few milliseconds, until the fadeOut has been completed. '''Rotating elements''' You can check here [http://jimfulton.info/demos/dojo-animated-rotate.html an example of use] of Dojo to make an element rotate. This example combines "Dojo.Animation" method and a CSS3 property that allow you to rotate the element. IMPORTANT: to asses browser compatibility, you must select the CSS property to use just like in the example (see sourcecode below): <pre> var transform; dojo.forEach( ['transform', 'WebkitTransform', 'msTransform', 'MozTransform', 'OTransform'], function (name) { if (typeof dojo.body().style[name] != 'undefined') { transform = name; } }); // ... and then use "transform" as the name of your CSS property for rotation </pre> === Moving elements === '''this.placeOnObject( mobile_obj, target_obj )''' placeOnObject works exactly like "slideToObject", except that the effect is immediate. This is not really an animation, but placeOnObject is frequently used before starting an animation. Example: <pre> // (We just created an object "my_new_token") // Place the new token on current player board this.placeOnObject( "my_new_token", "overall_player_board_"+this.player_id ); // Then slide it to its position on the board this.slideToObject( "my_new_token", "a_place_on_board" ).play(); </pre> '''this.placeOnObjectPos( mobile_obj, target_obj, target_x, target_y )''' This method works exactly like placeOnObject, except than you can specify some (x,y) coordinates. This way, "mobile_obj" will be placed to the specified x,y position relatively to "target_obj". '''this.attachToNewParent( mobile_obj, target_obj )''' With this method, you change the HTML parent of "mobile_obj" element. "target_obj" is the new parent of this element. The beauty of attachToNewParent is that the mobile_obj element DOES NOT MOVE during this process. Note: what happens is that the method calculate a relative position of mobile_obj to make sure it does not move after the HTML parent changes. Why using this method? Changing the HTML parent of an element can be useful for the following reasons: * When the HTML parent moves, all its child are moving with them. If some game elements is no more linked with a parent HTML object, you may want to attach it to another place. * The z_order (vertical order of display) depends on the position in the DOM, so you may need to change the parent of some game elements when they are moving in your game area. CAREFUL: when you attach an HTML element with a new parent, you break all references to this HTML element (ex: dojo.connect). == Players input == '''dojo.connect''' Used to associate a player event with one of your notification method. Example: associate a click on an element ("my_element") with one of our method ("onClickOnMyElement"): <pre> dojo.connect( $('my_element'), 'onclick', this, 'onClickOnMyElement' ); </pre> Note: this is the only possible correct way to associate a player input event to your code, and you must not use anything else. '''this.checkAction( "my_action_name" )''' Usage: checkAction: function( action, nomessage ) Check if player can do the specified action by taking into account: * current game state * interface locking (a player can't do any action if an action is already in progress) return true if action is authorized (ie: the action is listed as a "possibleaction" in current game state). return false and display an error message if not (display no message if nomessage parameter is true). The displayed error message could be either "This move is not allowed at this moment" or "An action is already in progress". Example: <pre> function onClickOnGameElement( evt ) { if( this.checkAction( "my_action" ) ) { // Do the action } } </pre> '''this.ajaxcall( url, parameters, obj_callback, callback, callback_error )''' This method must be used to send a player input to the game server. * url: the url of the action to perform. For a game, it must be: "/<mygame>/<mygame>/myAction.html" * parameters: an array of parameter to send to the game server. Note that "lock:true" must always be specified in this list of parameter in order the interface can be locked during the server call. * obj_callback: must be set to "this". * callback: a function to trigger when the server returns and everything went fine. * callback_error: (optional and rarely used) a function to trigger when the server returns an error. Usage: <pre> this.ajaxcall( '/mygame/mygame/myaction.html', { lock: true, arg1: myarg1, arg2: myarg2, ... }, this, function( result ) { // Do some stuff after a successful call } ); </pre> Restricted arguments names (please don't use them): * "action" * "module" * "class" '''this.confirmationDialog()''' Display a confirmation dialog with a yes/no choice. We advice you to NOT use this function unless the player action is really critical and could ruins the game, because it slows down the game and upset players. Usage: this.confirmationDialog( "Question to displayed", callback_function_if_click_on_yes ); Example: <pre> this.confirmationDialog( _('Are you sure to use this bonus (points penalty at the end of the game) ?'), dojo.hitch( this, function() { this.ajaxcall( '/seasons/seasons/useBonus.html', { id:bonus_id, lock:true }, this, function( result ) {} ); } ) ); </pre> ; addEventToClass: function( cssClassName, eventName, functionName ) : Same as dojo.connect(), but for all the nodes set with the specified cssClassName '''this.addActionButton( id, label, method, (opt)depreciated, (opt)bHighlight )''' You can use this method to add an action button in the main action status bar. Arguments: * id: a ID that should be unique in your HTML DOM document. * label: the text of the button. Should be translatable (use _() function). * method: the name of your method that must be triggered when the player clicks on this button. * depreciated (optional): do not use this. Please not specify this argument or use "null". * bHighlight: if set to "true", the button is going blink to catch player's attention. Please don't abuse of blinking button. You should only use this method in your "onUpdateActionButtons" method. Usually, you use it like this (from Hears example): <pre> onUpdateActionButtons: function( stateName, args ) { console.log( 'onUpdateActionButtons: '+stateName ); if( this.isCurrentPlayerActive() ) { switch( stateName ) { case 'giveCards': this.addActionButton( 'giveCards_button', _('Give selected cards'), 'onGiveCards' ); break; } } }, </pre> In the example above, we are adding a "Give selected cards" button in the case we are on game state "giveCards". When player clicks on this button, it triggers our "onGiveCards" method. == Translations == See [[Translations]] == Notifications == When something happens on the server side, your game interface Javascript logic received a notification. Here's how you can handle these notifications on the client side. === Subscribe to notifications === Your Javascript "setupNotifications" method is the place where you can subscribe to notifications from your PHP code. Here's how you associate one of your Javascript method to a notification "playDisc" (from Reversi example): <pre> // In setupNotifications method: dojo.subscribe( 'playDisc', this, "notif_playDisc" ); </pre> Note: the "playDisc" corresponds to the name of the notification you define it in your PHP code, in your "notifyAllPlayers" or "notifyPlayer" method. Then, you have to define your "notif_playDisc" method: <pre> notif_playDisc: function( notif ) { // Remove current possible moves (makes the board more clear) dojo.query( '.possibleMove' ).removeClass( 'possibleMove' ); this.addDiscOnBoard( notif.args.x, notif.args.y, notif.args.player_id ); }, </pre> In a notification handler like our "notif_playDisc" method, you can access to all notifications arguments with "notif.args". Example: <pre> // If you did this on PHP side: self::notifyAllPlayers( "myNotification", '', array( "myArgument" => 3 ) ); // On Javascript side, you can access the "myArgument" like this: notif_myNotification: function( notif ) { alert( "myArgument = " + notif.args.myArgument ); } </pre> === Synchronous notifications === When several notifications are received by your game interface, these notifications are processed immediately, one after the other, in the same exact order they have been generated in your PHP game logic. However, sometimes, you need to give some time to the players to figure out what happened on the game before jumping to the next notification. Indeed, in many games, they are a lot of automatic actions, and the computer is going to resolve all these actions very fast if you don't tell it not to do so. As an example, for Reversi, when someone is playing a disc, we want to wait 500 milliseconds before doing anything else in order the opponent player can figure out what move has been played. Here's how we do this, right after our subscription: <pre> dojo.subscribe( 'playDisc', this, "notif_playDisc" ); this.notifqueue.setSynchronous( 'playDisc', 500 ); // Wait 500 milliseconds after executing the playDisc handler </pre> == Tooltips == '''this.addTooltip( nodeId, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to the DOM node. Specify 'helpString' to display some information about "what is this game element?". Specify 'actionString' to display some information about "what happens when I click on this element?". You must specify both helpString and actionString. Most of the time, you use only one and specify a void string ("") for the other one. Usually, _() must be used for the text to be marked for translation. "Delay" is an optional parameter. Usually, it is primarily used to specify a zero delay for some game element when the tooltip gives really important information for the game - but remember: no essential information must be placed in tooltips as they won't be displayed in some browser (see Guidelines). Example: <pre> this.addTooltip( 'cardcount', _('Number of cards in hand'), '' ); </pre> '''this.addTooltipHtml( nodeId, html, delay )''' Add an HTML tooltip to the DOM node (for more elaborate content such as presenting a bigger version of a card). '''this.addTooltipToClass( cssClass, _( helpString ), _( actionString ), delay )''' Add a simple text tooltip to all the DOM nodes set with this cssClass. IMPORTANT: all concerned nodes must have IDs to get tooltips. '''this.addTooltipHtmlToClass( cssClass, html, delay )''' Add an HTML tooltip to to all the DOM nodes set with this cssClass (for more elaborate content such as presenting a bigger version of a card). IMPORTANT: all concerned nodes must have IDs to get tooltips '''this.removeTooltip( nodeId )''' Remove a tooltip from the DOM node. == Dialogs, warning messages, confirmation dialogs, ... == === Warning messages === Sometimes, there is something important that is happening on the game and you have to make sure all players get the message. Most of the time, the evolution of the game situation or the game log is enough, but sometimes you need something more visible. Ex: someone fulfill one of the end of the game condition, so this is the last turn. '''this.showMessage( msg, type )''' showMessage shows a message in a big rectangular area on the top of the screen of current player. * "msg" is the string to display. It should be translated. * "type" can be set to "info" or "error". If set to "info", the message will be an informative message on a white background. If set to "error", the message will be an error message on a red background. Important: the normal way to inform players about the progression of the game is the game log. "showMessage" is intrusive and should not be used often. === Confirmation dialog === When an important action with a lot of consequences is triggered by the player, you may want to propose a confirmation dialog. CAREFUL: the general guidelines of BGA is to AVOID the use of confirmation dialog. Confirmation dialogs slow down the game and bother players. The players knows that they have to pay attention about each move when they are playing online. The situation where you should use a confirmation dialog are the following: * It must not happen very often during a game. * It must be linked to an action that can really "kill a game" if the player do not pay attention. * It must be something that can be done by mistake (ex: a link on the action status bar). How to display a confirmation dialog: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> === Dialogs === At first, you shouldn't use dialogs windows. BGA guidelines specify that all game elements should be displayed on the main screen. Players can eventually scroll down to see game elements they don't need to see anytime, and you may eventually create anchors to move between game area section. Of course dialogs windows are very practical, but the thing is: all players know how to scroll down, and not all players know how to show up your dialog window. In addition, when the dialog shows up, players can't access the other game components. Sometimes although, you need to display a dialog window. Here is how you do this: <pre> // Create the new dialog. You should store the handler in a member variable to access it later this.myDlg = new dijit.Dialog({ title: _("my dialog title to translate") }); // Create the HTML of my dialog. The best practice here is to use [[Game_layout:_view_and_template:_yourgamename.view.php_and_yourgamename_yourgamename.tpl#Javascript_templates|Javascript templates]]: var html = this.format_block( 'jstpl_myDialogTemplate', { arg1: myArg1, arg2: myArg2, ... } ); // Show the dialog this.myDlg.attr("content", html ); this.myDlg.show(); // Now that the dialog has been displayed, you can connect your method to some dialog elements // Example, a "close" button: dojo.connect( $('closeDlg'), 'onclick', this, function(evt){ evt.preventDefault(); this.myDlg.hide(); } ); </pre> Tip: be careful with "hide()" method to close your dialog: the dialog and its content is not completely removed from the DOM. It can cause you problems if you try to display the same dialog several times. A good practice is to wrap all the content of your dialog in a "<div id='myDlgContent'>" div element, and to call "dojo.destroy('myDlgContent')" before displaying your dialog. === Scoring dialogs === Sometimes at the end of a round you want to display a big table that details the points wins in each section of the game. Example: in Hearts game, we display at the end of each round the number of "heart" cards collected by each player, the player who collected the Queen of Spades, and the total number of points loose by each player. Scoring dialogs are managed entirely on '''PHP side''', but they are described here as their effects are visible only on client side. Displaying a scoring dialog is quite simple and is using a special notification type: "tableWindow": <pre> // on PHP side: $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table ) ); </pre> The "table" argument is a 2 dimensional PHP array that describe the table you want to display, line by line and column by column. Example: display an 3x3 array of strings <pre> $table = array( array( "one", "two", "three" ), // This is my first line array( "four", "five", "six" ), // This is my second line array( "seven", "height", "nine" ) // This is my third line ); </pre> As you can see above, in each "cell" of your array you can display a simple string value. But you can also display a complex value with a template and associated arguments like this: <pre> $table = array( array( "one", "two", array( "str" => "a string with an ${argument}", "args" => array( 'argument' => 'argument_value' ) ) ), array( "four", "five", "six" ), array( "seven", "height", "nine" ) ); </pre> This is especially useful when you want to display player names with colors. Example from "Hearts": <pre> $firstRow = array( '' ); foreach( $players as $player_id => $player ) { $firstRow[] = array( 'str' => '${player_name}', 'args' => array( 'player_name' => $player['player_name'] ), 'type' => 'header' ); } $table[] = $firstRow; </pre> You can also use three extra attributes in the parameter array for the notification: <pre> $this->notifyAllPlayers( "tableWindow", '', array( "id" => 'finalScoring', "title" => clienttranslate("Title of the scoring dialog"), "table" => $table, "header" => '<div>Some header</div>', "footer" => '<div>Some footer</div>', "closelabel" => clienttranslate( "Closing button label" ) ) ); </pre> *'''header''': the content for this parameter will display before the table (also, the html will be parsed and player names will be colored according to the current game colors) *'''footer''': the content for this parameter will display after the table (no parsing for coloring the player names) *'''closelabel''': if this parameter is used, a button will be displayed with this label at the bottom of the popup and will allow players to close it (more easily than by clicking the top right 'cross' icon). NB: this last parameter is not yet - 24/02/2014 - deployed on the studio, but can be used already and will take effect immediately with the next update. == Update players score == Increase a player score (with a positive or negative number): <pre> this.scoreCtrl[ player_id ].incValue( score_delta ); </pre> Set a player score to a specific value: <pre> this.scoreCtrl[ player_id ].setValue( new_score ); </pre> == Players panels == === Adding stuff to player's panel === At first, create a new "JS template" string in your template (tpl) file: (from Gomoku example) <pre> var jstpl_player_board = '\<div class="cp_board">\ <div id="stoneicon_p${id}" class="gmk_stoneicon gmk_stoneicon_${color}"></div><span id="stonecount_p${id}">0</span>\ </div>'; </pre> Then, you add this piece of code in your JS file to add this template to each player panel: <pre> // Setting up player boards for( var player_id in gamedatas.players ) { var player = gamedatas.players[player_id]; // Setting up players boards if needed var player_board_div = $('player_board_'+player_id); dojo.place( this.format_block('jstpl_player_board', player ), player_board_div ); } </pre> (Note: the code above is of course from your "setup" function in your Javascript). Very often, you have to distinguish current player and others players. In this case, you just have to create another JS template (ex: jstpl_otherplayer_board) and use it when "player_id" is different than "this.player_id". === Player's panel disabling/enabling === '''this.disablePlayerPanel( player_id )''' Disable given player panel (the panel background become gray). Usually, this is used to signal that this played passes, or will be inactive during a while. Note that the only effect of this is visual. There are no consequences on the behaviour of the panel itself. '''this.enablePlayerPanel( player_id )''' Enable a player panel that has been disabled before. '''this.enableAllPlayerPanels()''' Enable all player panels that has been disabled before. == Image loading == See also [[Game_art:_img_directory]]. '''Be careful''': by default, ALL images of your img directory are loaded on a player's browser when he loads the game. For this reason, don't let in your img directory images that are not useful, otherwise it's going to slowdown the game load. '''dontPreloadImage( image_file_name )''' Using dontPreloadImage, you tell the interface to not preload a specific image in your img directory. Example of use: <pre> this.dontPreloadImage( 'cards.png' ); </pre> This is particularly useful if for example you have 2 different themes for a game. To accelerate the loading of the game, you can specify to not preload images corresponding to the other theme. Another example of use: in "Gosu" game with Kamakor extension, you play with 5 sets of cards among 10 available. Cards images are organized by sets, and we only preload the images corresponding to the 5 current sets. '''Note:''' You don't need to specify to not preload game box images (game_box.png, game_box75.png...) since they are not preloaded by default. == Other useful stuff == '''dojo.hitch''' With dojo.hitch, you can create a callback function that will run with your game object context whatever happen. Typical example: display a BGA confirmation dialog with a callback function created with dojo.hitch: <pre> this.confirmationDialog( _('Are you sure you want to make this?'), dojo.hitch( this, function() { this.ajaxcall( '/mygame/mygame/makeThis.html', { lock:true }, this, function( result ) {} ); } ) ); </pre> In the example above, using dojo.hitch, we are sure that the "this" object will be set when the callback is called. ; updateCounters(counters) : Useful for updating game counters in the player panel (such as resources). : 'counters' arg is an associative array [counter_name_value => [ 'counter_name' => counter_name_value, 'counter_value' => counter_value_value], ... ] : All counters must be referenced in this.gamedatas.counters and will be updated. : DOM objects referenced by 'counter_name' will have their innerHTML updated with 'counter_value'. == BGA GUI components == BGA framework provides some useful ready-to-use components for the game interface: [[Studio#BGA_Studio_game_components_reference]] Note that each time you are using an additional component, you must declare it at the top of your Javascript file in the list of modules used. Example if you are using "ebg.stock": <pre> define([ "dojo","dojo/_base/declare", "ebg/core/gamegui", "ebg/counter", "ebg/stock" /// <=== we are using ebg.stock module ], </pre> 4f79eb54f61f8719ada11d802de902007c28bccb Getting started 0 10 1115 918 2018-06-04T17:22:27Z Leopoo 5546 wikitext text/x-wiki [[Category:Help]] With '''Board Game Arena''' you can play within a few clicks. By choosing "[http://en.boardgamearena.com/#!lobby Play now]" on the left menu, you get a list of game tables waiting for players. You can join a game by clicking on "View table" then "Join game". If you prefer, you can create a new game table: click on the "Launch table" button that corresponds to the game you want to play, and wait for your opponents. Important to know before you start: * It's much better to join a table already created than to create a new one. * For most popular games you will find opponents at any time. For the others, try to connect around peak hour (around 22h CEST) to maximize your chances. * When you start a game you can't leave the table until it ends. If you leave you will receive a penalty which will create difficulties finding opponents later. Sometimes the program throws out the game before it starts without telling you why. There's no way to know why, and FAQ's nor Getting Started won't help. We assume it has something to do some kind of time limits? 5570c50d88c9b3b4855b422337a0e22e7c65cc05 Gamehelpninemensmorris 0 164 1116 2020-04-08T06:47:34Z Startti joona 5607 Ak: Uusi sivu: Mylly Mylly (engl. Mill) on vanha strateginen lautapeli. Peli on suunnattu kahdelle pelaajalle. Pelin alkuperästä ei ole varmaa tietoa, mutta Egyptin Kurnasta löydetty kappal... wikitext text/x-wiki Mylly Mylly (engl. Mill) on vanha strateginen lautapeli. Peli on suunnattu kahdelle pelaajalle. Pelin alkuperästä ei ole varmaa tietoa, mutta Egyptin Kurnasta löydetty kappale on mahdollisesti varhainen mylly-pelin lauta. Pelin nimi ja säännöt vaihtelevat alueittain. Englanninkielellä se tunnetaan myös Nine Men's Morris -nimellä Pelilauta Muokkaa Pelilauta koostuu kolmesta sisäkkäisestä neliöstä, joiden kulma- ja sivujen keskipisteisiin pelaajat voivat laittaa nappuloita. Lisäksi neliöt ovat kytköksissä toisiinsa siten, että keskimmäisen neliön sivujen keskipisteet on yhdistetty ulommaisen ja sisimmäisen neliöiden vastaavien sivujen keskipisteisiin. Ulommaiselta neliöltä ei ole suoraa yhteyttä sisimpään neliöön. Säännöt Muokkaa Pelin alussa kumpikin pelaaja saa yhdeksän nappulaa, jotka sijoitetaan laudalle vuorotellen. Kun nappulat on vapaavalintaisesti sijoitettu laudalle, pelaajat koettavat vuorotellen siirtää nappuloita viivojen leikkauspisteistä toisiin viivoja pitkin. Siirron on päätyttävä johonkin lähimpään viivan leikkauspisteeseen, eli nappulaa ei saa kuljettaa ympäri lautaa yhdellä siirrolla. Jos pelaaja onnistuu siirtämään nappulan siten, että samalla viivalla on kolme saman pelaajan nappulaa, eli mylly, hän saa poistaa yhden sellaisen vastustajan nappulan, joka ei ole myllyssä. Paras tilanne on niin sanottu sahamylly, jossa pelaaja pystyy siirtämään yhtä nappulaa edestakaisin siten, että jokaisella siirrolla syntyy mylly. Useimmilla on tapana sanoa sulkiessaan myllyn: "Miilu kiinni, nappi pois!" Kun pelaajalla on jäljellä kolme nappulaa, voi nappula lentää siirrolla mihin tahansa laudalla vapaana olevaan ruutuun. Tämä antaa tasoitusta alakynnessä olevalle pelaajalle, mutta harvoin muuttaa pelin lopputulosta. Pelaaja voittaa mikäli hän onnistuu poistamaan 7 (vastustajalle jää vain 2 nappulaa jäljelle, eikä vastustaja voi enää muodostaa myllyä) vastustajan nappulaa laudalta. Myös jumittamalla vastustajan nappulat voi voittaa. 7935a191dde2b333dd789020f5c8a8e4e3b30df3 Gamehelpcolorpop 0 44 1117 181 2020-04-23T12:29:40Z Startti joona 5607 /* The game */ wikitext text/x-wiki Colorpop näyttää kuin videopeli, joka tuodaan lautapelimaailmaan, ja siinä on hieno laite, joka jäljittelee digitaalisten päälliköidemme tekemää työtä. Pelipöytä koostuu kaltevasta muovipidikkeestä, joka pitää kymmenen telinettä, ja kussakin telineessä on kymmenen värillistä merkkiä. Tokeneja on viidessä pelaajaväreissä (kummassakin 19) ja valkoisissa Jokereissa (5). Asenna peli liu'uttamalla rahakkeet telineisiin ja asettamalla sitten telineet pidikkeeseen siten, että värillä ei ole enemmän kuin viisi merkkiä, jotka ovat kohtisuorassa. Jokainen pelaaja saa sitten tunnuksen, joka paljastaa hänen pelinsä värin, merkin, jonka hän pitää piilossa muista pelaajista. Vuorollaan pelaaja valitsee ryhmän, joka koostuu kahdesta tai useammasta kohtisuoraan kytketystä merkistä ja työntää niitä, jotta ne putoavat telineiden läpi ja pitivät niitä alas. (Pelaaja voi tehdä jokerin mistä tahansa väristä, joten hän voi ottaa jokerin mukaan tai jättää sen pois haluamallaan vuorollaan.) Kaikki niiden yläpuolella olevat merkit liukuvat alas telineiden sisään. Soitin pitää kaikki merkit poissa. Pelaajat vuorotellen poistavat merkkejä, kunnes joko pelaaja poistetaan pelistä tai mitään kahden tai useamman tunnuksen ryhmiä ei enää ole. Tässä vaiheessa peli loppuu ja pelaajat paljastavat salaisuutensa. Jokainen, jolla on taululla vähiten väriä merkkejä, voittaa! Jos kahta tai useampaa pelaajaa on sidottu, he vertaa pelimerkkejä, jotka he ovat keränneet pelin aikana; kuka on kerännyt vähemmän omasta väristään, se voittaa. == Variants == '''Two secret colors''': this variant allows you to play Color Pop with two secret colors, which will have you thinking very often on which color you are gonna favor this turn! This variant is only available for two player games. NB : you can also '''play against the computer''' and try to win '''solo challenges''' on the [http://www.colorpop-online.com www.colorpop-online.com] website hosted by the game publisher, Gigamic. == Game preferences == The '''colorblind''' option available for this game enables an alternative setup displaying symbols on the tokens using the great color code designed by Miguel Neiva: [http://www.coloradd.net/code.asp ColorADD]. Learning the code is easy, just take a look at the following synthesis panel: [[File:ColorADD.jpg]] '''Have a good game!''' 28a8a009b736451a961b53024bc859f2bd2dfa9f Gamehelpsaintpoker 0 165 1120 2020-05-21T17:03:12Z Rexroom 5688 Initial translation wikitext text/x-wiki Pelaajia: 2-5 == Kuvaus == Löyhästi perustuen Texas hold ‘em pokeripeliin mutta ilman panos vaihetta. Peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa mistä tahansa korttiyhdistelmästä “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkorteista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata * Neljä samaa – neljä samanarvoista korttia * Täyskäsi – kolme samaa ja pari * Väri – kaikki kortit samaa maata * Suora – viisi korttia järjestyksessä, jotkut eri maata * Kolme samaa – kolme samanarvoista korttia * Kaksi paria – kaksi paria * Pari – kaksi samanarvoista korttia * Hai – ei mikään ylläolevista, käden korkein kortti * Huomautus 1: Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. * Huomautus 2: Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. a863bd9a593512614147ecd617160803da37afa4 1121 1120 2020-05-21T17:04:25Z Rexroom 5688 /* Säännöt */ wikitext text/x-wiki Pelaajia: 2-5 == Kuvaus == Löyhästi perustuen Texas hold ‘em pokeripeliin mutta ilman panos vaihetta. Peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata * Neljä samaa – neljä samanarvoista korttia * Täyskäsi – kolme samaa ja pari * Väri – kaikki kortit samaa maata * Suora – viisi korttia järjestyksessä, jotkut eri maata * Kolme samaa – kolme samanarvoista korttia * Kaksi paria – kaksi paria * Pari – kaksi samanarvoista korttia * Hai – ei mikään ylläolevista, käden korkein kortti * Huomautus 1: Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. * Huomautus 2: Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. 121a02c7584d2f69dd2a459872f5d74dee8c433c 1122 1121 2020-05-21T17:23:14Z Rexroom 5688 /* Kuvaus */ wikitext text/x-wiki Pelaajia: 2-5 == Kuvaus == Löyhästi perustuen Texas hold ‘em pokeripeliin mutta ilman panosvaihetta. Peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata * Neljä samaa – neljä samanarvoista korttia * Täyskäsi – kolme samaa ja pari * Väri – kaikki kortit samaa maata * Suora – viisi korttia järjestyksessä, jotkut eri maata * Kolme samaa – kolme samanarvoista korttia * Kaksi paria – kaksi paria * Pari – kaksi samanarvoista korttia * Hai – ei mikään ylläolevista, käden korkein kortti * Huomautus 1: Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. * Huomautus 2: Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. 189fb72e9720caf663f8f8fb1400517d66c3a995 1123 1122 2020-05-21T17:32:52Z Rexroom 5688 wikitext text/x-wiki Löyhästi perustuen Texas hold ‘em pokeripeliin mutta ilman panosvaihetta. Tätä 2-5 pelaajan peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata * Neljä samaa – neljä samanarvoista korttia * Täyskäsi – kolme samaa ja pari * Väri – kaikki kortit samaa maata * Suora – viisi korttia järjestyksessä, jotkut eri maata * Kolme samaa – kolme samanarvoista korttia * Kaksi paria – kaksi paria * Pari – kaksi samanarvoista korttia * Hai – ei mikään ylläolevista, käden korkein kortti * Huomautus 1: Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. * Huomautus 2: Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. fb244ecdf758d008a5e91598ed487662f1b7849d 1124 1123 2020-05-21T17:46:09Z Rexroom 5688 /* Käsien arvojärjestys */ wikitext text/x-wiki Löyhästi perustuen Texas hold ‘em pokeripeliin mutta ilman panosvaihetta. Tätä 2-5 pelaajan peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata: ♢4 ♢5 ♢6 ♢7 ♢8 * Neljä samaa – neljä samanarvoista korttia: ♡9 ♠9 ♢9 ♣9 * Täyskäsi – kolme samaa ja pari: ♡8 ♠8 ♣8 ♡2 ♣2 * Väri – kaikki kortit samaa maata: ♢K ♢J ♢9 ♢6 ♢4 * Suora – viisi korttia järjestyksessä, jotkut eri maata: 2♣ ♡3 4♢ 5♠ 6♣ * Kolme samaa – kolme samanarvoista korttia: 8♣ 8♢ 8♠ * Kaksi paria – kaksi paria: 9♣ 9♡ 5♠ 5♢ * Pari – kaksi samanarvoista korttia: 7♣ 7♢ * Hai – ei mikään ylläolevista, käden korkein kortti '''Huom 1''' Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. '''Huom 2''' Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. bf71d9e5ac9f4430547adf549ac822c9184ce448 1125 1124 2020-05-21T18:08:07Z Rexroom 5688 wikitext text/x-wiki Löyhästi Texas hold ‘em pokeripeliin perustuva mutta ilman panosvaihetta oleva 2-5 pelaajan korttipeli. Peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata: ♢4 ♢5 ♢6 ♢7 ♢8 * Neljä samaa – neljä samanarvoista korttia: ♡9 ♠9 ♢9 ♣9 * Täyskäsi – kolme samaa ja pari: ♡8 ♠8 ♣8 ♡2 ♣2 * Väri – kaikki kortit samaa maata: ♢K ♢J ♢9 ♢6 ♢4 * Suora – viisi korttia järjestyksessä, jotkut eri maata: 2♣ ♡3 4♢ 5♠ 6♣ * Kolme samaa – kolme samanarvoista korttia: 8♣ 8♢ 8♠ * Kaksi paria – kaksi paria: 9♣ 9♡ 5♠ 5♢ * Pari – kaksi samanarvoista korttia: 7♣ 7♢ * Hai – ei mikään ylläolevista, käden korkein kortti '''Huom 1''' Tasapelien sattuessa kuten värien kanssa korkein kortti voittaa. Ässä on korkein. '''Huom 2''' Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljellejääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. fe8dab2b744ccc42acd3ee7cfc6f55379325a7e6 1133 1125 2020-05-22T09:10:36Z Rexroom 5688 wikitext text/x-wiki Löyhästi Texas hold ‘em pokeripeliin perustuva mutta ilman panosvaihetta oleva 2-5 pelaajan korttipeli. Peliä pelataan usealla kierroksella ja sisältää enemmän strategista päätöksentekoa, koska sinulla on enemmän tietoa korteista ja vaikutusvaltaa niihin. == Peli == === Komponentit === * Angloamerikkalainen 52 kortin pakka * Muistiinpanovälineet pistelaskua varten === Säännöt === Jokaisen kierroksen lopussa jokainen pelaaja tekee 5 kortin pokerikäden Texas hold’em pokerin vakiosäännöillä. Pelaajan 5 korttia voidaan rakentaa “joesta” (pelaajien yhteisestä avonaisesta korttikädestä keskellä pöytää) ja 2 yksittäisestä pöytäkortista minkä pelaaja on valinnut kädestään. Jokaisen pelaajan pokerikäsi sitten pisteytetään löytääkseen kierroksen voittajan. === Käsien arvojärjestys === * Värisuora – viisi korttia järjestyksessä, kaikki samaa maata: ♢4 ♢5 ♢6 ♢7 ♢8 * Neljä samaa – neljä samanarvoista korttia: ♡9 ♠9 ♢9 ♣9 * Täyskäsi – kolme samaa ja pari: ♡8 ♠8 ♣8 ♡2 ♣2 * Väri – kaikki kortit samaa maata: ♢K ♢J ♢9 ♢6 ♢4 * Suora – viisi korttia järjestyksessä, jotkut eri maata: 2♣ ♡3 4♢ 5♠ 6♣ * Kolme samaa – kolme samanarvoista korttia: 8♣ 8♢ 8♠ * Kaksi paria – kaksi paria: 9♣ 9♡ 5♠ 5♢ * Pari – kaksi samanarvoista korttia: 7♣ 7♢ * Hai – ei mikään ylläolevista, käden korkein kortti '''Huom 1''' Tasapelien sattuessa, kuten värien tapauksessa, korkein kortti voittaa. Ässä on korkein. '''Huom 2''' Tasapeleissä, jossa on 5 korttia vähemmän yhdistelmiä, jäljelle jääneen kortin arvo ratkaisee. === Esimerkkikierros === Voittaja: Pelaaja B voittaa värisuoralla 2. Sija: Pelaaja C:llä on kuningas väri hertoilla 3. Sija: Pelaaja A tulee seuraavana suoralla 4. Sija: Pelaaja D tulee viimeisenä (heikommalla) suoralla === Alkujärjestelyt === Sekoita pakka. Jaa 10 korttia jokaiselle pelaajalle. Seuraavaksi nosta tietty määrä kortteja riippuen pelaajamäärästä, näistä muodostuu “tulevan joen” kortit. 3 korttia kaksinpelissä. 2 korttia kolmen pelissä, 1 kortti nelinpelissä, 0 korttia viiden pelaajan pelissä. == Pelaaminen == === Jokivaihe === Siirrä kortit tulevasta joesta keskelle pöytää, muodostaen täten nykyisen joen alun. Nosta pakasta korvaava tuleva joki samalla määrällä kortteja. Viimeistelläksesi nykyisen joen, jokaisen pelaajan pitää valita 1 kortti kädestä ja samanaikaisesti paljastaa ne. Keskellä pöytää pitäisi olla 5 kuvapuoli ylöspäin olevaa korttia. === Paljastus ja pisteytysvaihe === Jokainen pelaaja valitsee 2 korttia kädestään. Pelaajat paljastavat heidän 2 korttiaan samanaikaisesti. Päätelkää pokeripelin sääntöjen mukaisesti voittojärjestys. Voittaja saa pisteitä pelatun kierroksen mukaisesti. Eli 1 piste 1. kierroksesta, 2 pistettä 2. kierroksesta jne. Pitäkää lukua pistemääristä, kaikilla on vapaus tietää senhetkinen pistetilanne. === Nosto === Käänteisellä voittojärjestyksellä pelaajat ottavat vuorotellen kortin joko joesta tai pelaajien pelatuista korteista ja pistävät sen omaan käteensä, voittajan ollessa viimeinen, joka ottaa kortin. Toistakaa vielä kerran että jokainen on poiminut 2 korttia. Poistakaa jäljelle jääneet 5 korttia pelistä, niitä ei enää käytetä. Toistakaa 8 kierroksen verran. === Voittaja === Voittaja on se, jolla on eniten pisteitä pelin lopussa. == Huomautukset == Joskus tulee tasapelejä voittokäsien suhteen. Silloin pisteet jaetaan voittajien kesken pyöristäen ylös. Nostovaiheessa sitten eniten pisteitä haalinut valitsee viimeisenä. Jos siinäkin on tasan niin nuorin pelaaja nostaa ensimmäisenä. 887677f379c230fa193f70fed2f164c5e09b4c58 Gamehelpconnectfour 0 166 1126 2020-05-21T18:27:57Z Rexroom 5688 Ak: Uusi sivu: Yritä luoda ensimmäisenä neljän suoraa pudottamalla pelinappuloitasi sarakkeisiin vuorotellen vastustajasi kanssa. Neljän suora voi olla pysty-, vaakasuoraan tai vinottain. [... wikitext text/x-wiki Yritä luoda ensimmäisenä neljän suoraa pudottamalla pelinappuloitasi sarakkeisiin vuorotellen vastustajasi kanssa. Neljän suora voi olla pysty-, vaakasuoraan tai vinottain. [[Category:Peliohjeet]] f6b4201630875e0692a21528203df35bc5b2d43e Ohje 0 167 1127 2020-05-21T18:49:21Z Rexroom 5688 Ak: Uusi sivu: == Peliohjeet ja vinkit == === Pelattavana === * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * Gam... wikitext text/x-wiki == Peliohjeet ja vinkit == === Pelattavana === * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpfrenchtarot|French Tarot]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] '''TÄMÄ EI OLE UNO''' * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] === Kehityksessä === * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] ce074147945fffac715d9e419afacb4898e59442 1132 1127 2020-05-22T07:55:39Z Rexroom 5688 /* Peliohjeet ja vinkit */ wikitext text/x-wiki == Peliohjeet ja vinkit == === Pelattavana === {| |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpfrenchtarot|French Tarot]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] 98e5b9eadb5c71cbb0728b9d56e4e01c9f8db623 1135 1132 2020-05-25T19:50:28Z Rexroom 5688 Lokalisointi-osio wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! A-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpfrenchtarot|French Tarot]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] c7e9be9357a2bb618f541777fa9b02b86ed17f1f 1137 1135 2020-05-25T20:21:19Z Rexroom 5688 /* Pelattavana */ Mylly aakkosjärjestykseen wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! A-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpfrenchtarot|French Tarot]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] 06ae597ccf6b761370b7eca178534865301b8106 1139 1137 2020-05-28T08:33:33Z Rexroom 5688 /* Lokalisaatio */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! A-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpfrenchtarot|French Tarot]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] aef079c35f9b9eb64dcc97efd2d519a531296999 1140 1139 2020-05-28T08:43:24Z Rexroom 5688 /* Peliohjeet */ Beta-pelit wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] c2cb1a2229494347f3679581fb99b4113aa7bfac 1146 1140 2020-05-28T21:04:30Z Rexroom 5688 /* Kehityksessä */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Hearts]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpohhell|Oh Hell!]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yahtzee]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] d88d4334ddc732bd578a87848d575ec2bb72bbc2 1147 1146 2020-05-29T06:41:54Z Rexroom 5688 /* Pelattavana */ Suomennosten lisääminen wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] bb394aa712dec67e739c66069a87420ab930c68e 1148 1147 2020-05-29T07:01:47Z Rexroom 5688 /* Lokalisaatio */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisaatio == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[SinäPelaaja]] – miten ${you}-määritelmä pitäisi ratkaista == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] 47d682af1e59f0e083ac56b25079d6879aabc09d 1149 1148 2020-05-29T07:04:12Z Rexroom 5688 /* Lokalisaatio */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] a03d947e857e956bc3abc8eae1f610659f6fa4e1 Etusivu 0 168 1128 2020-05-21T18:50:30Z Rexroom 5688 Uudelleenohjaus wikitext text/x-wiki #REDIRECT [[Ohje]] 6bf6ccd16b9dc38a7bc05b65e6079a101b92e6b7 Gamehelpseasons 0 43 1129 900 2020-05-21T18:52:28Z Rexroom 5688 wikitext text/x-wiki Seasons is a game of generating points (crystals) by choosing the correct combination of dice and the activation of card powers. The game goes by each player separating their 9 cards into 3 packs of 3 cards. The card will be returned to the player on the starting of each cycle. Each player can choose only one die per turn. The effects are as follows: ''Star'' - increase the maximum card player can summon (max 15) ''Elements'' - Gain an energy of the element shown (water, earth, air, fire) ''Numbers'' - Gain the number of Crystals as indicated by the number ''Square card'' - Draw a card ''Dice with frames surrounding'' - Allow user to transmute energy into crystal (depend on which part of the game is at) ''Dots'' - Indicate how fast the marker progresses through the seasons cycle There is also a maximum of 3 helps provided at the cost of point deduction at the end of the game. The help will cost 5 points for the 1st time, 7 for the 2nd and 8 for the 3rd. There are 4 type of helps which include adding a star, allow user to transmute this turn with an additional crystal gained for each energy transmuted, change 2 energy into any element and allow user to choose a card out of 2 cards (must use with draw card dice). At the end of the game the points are calculated by adding the numbers on each played card and the crystal owned and substracting 5 points for each power card remaining in hand. The player with the most victory points wins. Note: Users need to discard energy immediately if the energy is more than the slots allocated. If a card allows you to summon another card but you have no space for this second card then it (the second card) will be discarded with no refund. == Difficulty levels: == '''Apprentice wizard level''' In order to make the game easier to learn, there are “pre-constructed” sets of nine power cards. Instead of taking step 1 of the first game phase, each player gets one of these sets. '''Magician level''' Only the power cards numbered 1 to 30 are in play. These cards have easy to grasp effects and will allow you to slowly discover the world of Seasons. '''Archmage level''' All 50 kinds of power cards are in play. The cards numbered 31 through 50 have more complex effects than the basic cards, but will allow you to extend the fun of playing by discovering new effects and combinations. [[Category:Käännettävä]] 47be95616e7f6547c386dc9040375e36e8d90cce Luokka:Käännettävä 14 169 1130 2020-05-21T18:53:17Z Rexroom 5688 Ak: Uusi sivu: Sivut, jotka eivät ole suomeksi ja kaipaavat kääntämistä. wikitext text/x-wiki Sivut, jotka eivät ole suomeksi ja kaipaavat kääntämistä. f79ae8d45ca7664cf9d638e44a38b137882caa43 Gamehelpcantstop 0 35 1131 913 2020-05-21T18:54:25Z Rexroom 5688 Category-lisäys wikitext text/x-wiki Can't Stop is a game involving rolling dice and knowing when to stop pushing one's luck. ==Rules== Can't Stop is played with a board consisting of 11 columns, numbered from 2 to 12, four dice, and three temporary pieces. ===Splitting dice=== On a player's turn, they roll the four dice, split the four dice to two groups of two dice however they like, then advance a step on each column whose sum is equal to some group of the dice. Examples: * Roll: 3,3,4,4. One can split it to 3,3 and 4,4, advancing a step on each of columns 6 and 8, or 3,4 and 3,4, advancing two steps on column 7. * Roll: 2,3,4,5. One can split it in three ways: 2,3 and 4,5 (columns 5 and 9), 2,4 and 3,5 (columns 6 and 8), or 2,5 and 3,4 (columns 7 and 7). * Roll: 4,4,4,6. There is only one way to split it, 4,4 and 4,6, which means columns 8 and 10. * Roll: 1,1,1,1. There is only one way to split it, 1,1 and 1,1, which means columns 2 and 2. ===Temporary pieces=== In a turn, a player may only advance in three columns. The temporary pieces are to help the player in keeping track their progresses. However, one must make as many moves as possible. Examples: * One hasn't moved at all and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 5,9. They may not choose to advance only on column 5 or only on column 9 because they must perform all moves possible. * One has moved on columns 4 and 5 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, advancing on columns 5 and 9. This is allowed; they advanced on columns 4,5,9. * One has moved on columns 2 and 3 and rolled 2,3,4,5. They decide to split as 2,3 and 4,5, and so should have advanced on columns 5 and 9. But this is not allowed; it makes them advance on four columns. They must pick either 5 or 9 to advance from. * One has moved on columns 2, 3, and 12 and rolled 2,3,4,5. No matter how they split the dice, they cannot advance. ===Stopping one's turn=== A player keeps their turn until they state to stop or they cannot advance (like the last of the examples above). In the former case, the temporary pieces are made permanent; they will continue from the positions they have now. In the latter case, the temporary pieces are removed; their position is rolled back to before they have started their turn. Examples: * One decides to stop after advancing 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9. Now their position is 4 steps on column 6, 3 steps on column 7, 2 steps on column 8, and 4 steps on column 9. * One advanced 3 steps on column 7, 2 steps on column 8, and 1 step on column 9, after previously have advanced 4 steps on column 6 and 3 steps on column 9, but then cannot advance. Their position is rolled back to only 4 steps on column 6 and 3 steps on column 9. ===Completing a column=== When one decides to stop and they have reached the end of some column, that column is scored for that player and no other player may advance in that column. For example, if column 5 has been scored and one splits a 2,3,4,5 to 2,3 and 4,5, they cannot advance on column 5 because it has been scored. ===Winning condition=== When one scores three columns, they win. ==Strategy== The strategy of the game revolves on knowing when to stop. As one keeps playing, they still have the risk of losing all progress on the turn; stopping will save the progress but gives the other players a turn each. Knowing which is more beneficial helps. Columns with more extreme numbers are less probable (a 2 is only scored by a 1,1 and a 12 is only scored by a 6,6, while a 7 is scored by six pairs of dice), so they have less steps to work on. One's strategy can also revolve on choosing whether to aim for middle columns or extreme columns. [[Category:Käännettävä]] c96632703ac8429cd4a2ef91965bc2e21a2b4b3b Gamehelpsaboteur 0 32 1134 1025 2020-05-25T19:11:54Z Rexroom 5688 Lyhennys ja luokittelu wikitext text/x-wiki == Goal == Get as many gold nuggets as possible during the three rounds of the game. In order to do so, # if you are a '''gold digger''', you must, in association with other gold diggers, build a path from the 'Start' card to the treasure card which can be found among the three 'End' cards # if you are a '''saboteur''', you must, in association with other saboteurs, prevent the gold diggers from getting to the treasure. Your role (gold digger or saboteur) will be randomly selected at the start of each round. == Rules summary == On your turn, you must click on a card from your hand to select it, then play this card or discard it. You can also rotate a 'Path' card before playing it by clicking the 'rotate' arrow that appears above the card. The cards are of several types: * 'Path' card: you can play this card to extend the maze, provided it is compatible with the cards already in place. To do this, click the location where you want to put the card. * 'Sabotage' card: you can break another player's tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score). * 'Repair' card: you can repair your, or another player's, broken tool of the type indicated. To do this, click the corresponding tool in the target player's panel (under the score), or click on the 'Sabotage' card in front of you. * 'Map' card: you can play this card on any 'End' card to discover whether or not the treasure lies there (you alone will get the information, other players will see nothing). Just click on the 'End' card that you want to know everything about. * 'Rock fall' card: this card lets you remove any 'Path' card of the maze. Just click on the card you want to remove. == Cards in play == * 44 'Path' cards * 9 'Sabotage' cards (three for each tool) * 6 'Repair a tool' cards (two for each tool) * 3 'Repair a tool among these two' cards (one for each combination of two tools) * 5 'Map' cards (one less than in the box set, as requested by the game author) * 3 'Rock fall' cards * 28 'Gold' cards ** 16 with one gold nugget ** 8 with two gold nuggets ** 4 with three gold nuggets == Roles == Roles are randomly selected among a set that depends upon the number of players: * with 3 players: 1 saboteur and 3 gold diggers * with 4 players: 1 saboteur and 4 gold diggers * with 5 players: 2 saboteurs and 4 gold diggers * with 6 players: 2 saboteurs and 5 gold diggers * with 7 players: 3 saboteurs and 5 gold diggers * with 8 players: 3 saboteurs and 6 gold diggers * with 9 players: 3 saboteurs and 7 gold diggers * with 10 players: 4 saboteurs and 7 gold diggers == Available variants == The game author, '''Frederic Moyersoen''', told us about some game variants, and we implemented all four of them. Please tell us which one is your favourite in the forum! Two variants are about gold sharing: * '''Old Mine''': the old mine was not as packed with gold... sometimes all you got for your digging was worthless stones. In this variant, some 'Gold' cards do not yield any gold nuggets (among the 28 'Gold' cards, 4 are worth three nuggets, 8 are worth two nuggets, 10 are worth one nugget and 6 are worthless). So it is definitely best to get first to the treasure in order to be sure not to leave empty-handed! * '''New mine''': the new mine is more even to each gold digger. Instead of distributing as many 'Gold' cards as the number of players - which benefits most to the gold digger that gets first to the treasure as he often gets two 'Gold' cards - it distributes as many 'Gold' cards as the number of gold diggers in play. Therefore, each gold digger will get only one 'Gold' card (however, it is still best to be first go get to the treasure in order to get a card of higher value!) Two variants are about gameplay: * '''Competitive''': the gold diggers who have a 'Sabotage' card (broken pickaxe, lamp or trolley) laid in front of them at the end of a round, do not receive a 'Gold' card when their team wins the round. The 'Gold' cards are distributed between the gold diggers who have no broken tool (saboteurs are not affected by this rule). Therefore, with this variant it can be interesting to sabotage some of your fellow gold diggers... but not too often, so as not to make your team lose! * '''Selfish dwarf''': one of the gold diggers will get a red jacket. This gold digger is a selfish dwarf: he can only win if he manages to complete the connection to the treasure himself. In this case, he gets four gold nuggets while the other players get nothing at all. If another gold digger completes the connection, the selfish dwarf takes no part in the treasure sharing (which is done with as many 'Gold' cards as the number of players, selfish dwarf excluded) [[Category:Käännettävä]] 94c3308ba93e736116d246aca34ddf09c922eb33 Suomentaminen 0 170 1136 2020-05-25T20:13:59Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Lue BGA-sivuston viralliset [https://boardgamearena.com/faq?anchor=faq_translations käännösohjeet]. == Syitä miksi kääntää suomeksi == * Helpompaa tutustuttaa ihmisiä uusiin peleihin * Kielimuurin alentaminen * Englannin kielen taito ei välttämättä riitä * jne. == Hyödyllisiä sivustoja == * [http://www.kielitoimistonohjepankki.fi/ Kielitoimiston ohjepankki] * [https://redfoxsanakirja.fi/ RedFox sanakirja] == ÄLÄ tee näitä == * Käännä pelin ''nimeä'' suomeksi ellei sillä ole virallista suomennosta * Jätä ''kontekstia'' huomioimatta * Ole liian kirjaimellinen suomennoksessa * Vaihda Solon nimeä UNOksi 6669a86fe1f3892da4b7c3b5198bc14c980ff48f Gamehelpreversi 0 124 1138 910 2020-05-25T20:41:44Z Rexroom 5688 Ohjeet kirjoitettu wikitext text/x-wiki Lautapeli kahdelle, jossa käännetään vastustajan laatat omalle värille. == Alkuasettelu == Asetetaan laudan keskelle 2x2 vuorotteleva kahden värin asetelma. Musta aloittaa. == Pelin kulku == * Pelaajan täytyy asettaa pelinappulansa kohtaan laudassa, jossa vastustajan nappulat jäävät pelaajan uuden ja vanhojen nappuloiden väliin. Välissä ei saa olla rakoja. * Nämä pelaajan nappuloiden väliin jäävät vastustajan nappulat käännetään ympäri, muuttaen ne pelaajan väriksi. * Kierros siirtyy vastustajalle. '''Huom!''' Jos pelaaja ei löydä paikkaa pelinappulalleen, hänen kierroksensa on ohi. == Pelin päättyminen == Peli päättyy, kun pelilaudalta ei löydy enää pelattavaa paikkaa. Voittaja on se osapuoli, jolla on eniten pelaajan väriä omaavia pelinappuloita laudalla. edb170561f2552b6b3d638bdab32073739fa340a Sanakirja 0 171 1141 2020-05-28T10:33:49Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === === B === === C === === D === === E === === F === === G === === H === === I === === J === === K === === L === === M === === N === === O === === P === === Q === === R === === S === === T === ;trick :tikki ;turn :vuoro ;turn order :vuorojärjestys === U === === V === === W === === X === === Y === === Z === 402bb5dc6b80dce7b486b75ce0d60e4198a905da 1142 1141 2020-05-28T17:53:03Z Rexroom 5688 /* Sanasto */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;trick :tikki ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === de7080fa4fe14b6a4c803a75ae0fb57242fc1442 1143 1142 2020-05-28T17:57:49Z Rexroom 5688 /* B */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;trick :tikki ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 0dfea3ba1f19c2ec7ebdb5c7e40be2f55d38891d 1145 1143 2020-05-28T20:00:35Z Rexroom 5688 /* T */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;trick :tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === c75e6a6fdc1b4b092620193547e1ae81e93c9088 Gamehelpchess 0 172 1144 2020-05-28T19:38:33Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Klassinen strateginen lautapeli kahdelle pelaajalle. == Päämäärä == Pelaajan on saatava vastustajansa kuningas tilanteeseen, jossa hänellä ei jää enää laillista siirtoa ilman, että hänen kuninkaansa syödään. == Nappulat == Nappulat eivät voi mennä muiden nappuloiden yli ellei toisin mainita. Vastustajan nappuloita "syödään" siirtämällä oma nappula samalle ruudulle kuin vastustajan, joka siirretään pois pelistä. === Kuningas === Liikkuu viistottain, vaaka- ja pystysuoraan 1 ruudun verran. === Kuningatar === Liikkuu viistottain, vaaka- ja pystysuoraan. === Torni === Liikkuu vaaka- ja pystysuoraan. ==== Linnoittautuminen ==== Kuningas voi vaihtaa jommankumman tornin kanssa paikkaa siirtymällä kaksi ruutua tornia kohti, jolloin torni siirtyy kuninkaan toiselle puolelle viereen. Liike on laillinen kun tietyt ehdot toteutuvat: # kuningas ja torni eivät ole liikkuneet vielä # kuningas, ylitettävä ruutu tai ruudut eikä siirtymäkohde ole uhattuna # kuninkaan ja tornin välillä ei ole muita nappuloita tiellä === Lähetti === Liikkuu viistottain suoraan. === Ratsu === Liikkuu 2 ruutua vaaka- tai pystysuoraan suuntaan '''ja''' yhden ruudun sivulle. '''Voi hypätä nappuloiden yli.''' === Sotilas === Liikkuu eteenpäin alkurivistä joko 1 tai 2 ruutua, mutta sen jälkeen vain yhden ruudun. Liikkuu viistosti 1 ruudun, jos siellä on vastustajan nappula. ==== Ohestalyönti ==== Jos vastustajan sotilas on siirtynyt 2 ruutua alkurivistään pelaajan sotilaan viereen, pelaaja voi tehdä ''ohestalyönnin'' ja viedä vastustajan sotilaan siirtämällä sotilaansa viistosti 1 ruudun alueelle kuin vastustaja olisi tehnyt vain 1 ruudun siirron. ''Tämä siirto on laillinen vain sen vuoron aikana.'' ==== Korotus ==== Sotilas, joka siirretään kauimpaan riviin, korotetaan samantien kuningattareksi, torniksi, lähetiksi tai ratsuksi. == Shakki ja matti == Shakki syntyy, kun vastapuolen nappula uhkaa kuningasta. ''Pelaajan on pakko saada kuningas pois tilanteesta.'' Matti syntyy, kun kuningasta ei saada pois shakista laillisin siirroin. === Tasapeli === Jos kumpikaan ei pysty tekemään mattia vastustajalleen erinäisistä syistä, pelaajat voivat sopia tasapelin. 43ece8e61f55653484a3d4ca50c006ca3597d2e5 Ongelmakohdat 0 173 1150 2020-05-29T07:45:10Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Listaus ongelmista lokalisoinneissa. == Käännöstyöt == === ${you}-määritelmän joustamattomuus === BGA:ssa ${you}-funktiolla on tietty ominaisuus: sen tarkoituksena on tekstiosiossa viitata pelaajaa sekä persoonapronominin ominaisuudella että pelaajan värillä: ::<code><span style="color: red">'''You'''</span> must choose a combination of dice</code> Tällä hetkellä tämä "you" on käännetty muotoon "sinä". Tämä ei sinällään ole huono vaihtoehto, ja tekstiä voidaan kääntää tämän mukaan. Ongelma on vain siinä, että useimmille kääntäjille ${you}-määritelmä jää pimentoon. Täten he saattavat kääntää mekaanisesti ylläolevan esimerkin muotoon: ::<code><span style="color: red">'''Sinä'''</span> täytyy valita noppayhdistelmä</code> Jotkut ovat jopa "ratkaisseet" tämän lisäämällä määritelmän loppuun n-kirjaimen: ::<code><span style="color: red">'''Sinä'''</span>n täytyy valita noppayhdistelmä</code> Yksi ratkaisu olisi kääntää tekstin äänensävy pakottavasta ("you must") mahdollistavaan ("you may"): ::<code><span style="color: red">'''Sinä'''</span> voit valita noppien yhdistelmän</code> Täten ongelmaa voidaan ratkaista kahdella tavalla: # Taivutetaan ${you} omistusmuotoon '''Sinun''' # Vaihdetaan käännettävän tekstin äänensävy Ensimmäisen vaihtoehdon ongelma on siinä, että se vie pohjan sen työn alta, joka on huomioinut määritelmän senhetkisen muodon. Toisen vaihtoehdon ongelma on sen kontekstiriippuvuus: voidaanko kaikkien tekstien äänensävyä vaihtaa ongelmitta? 249d8aed9c6749a96bbac104e6c6772a6665af69 1151 1150 2020-05-29T07:54:22Z Rexroom 5688 /* ${you}-määritelmän joustamattomuus */ wikitext text/x-wiki Listaus ongelmista lokalisoinneissa. == Käännöstyöt == === ${you}-määritelmän joustamattomuus === BGA:ssa ${you}-funktiolla on tietty ominaisuus: sen tarkoituksena on tekstiosiossa viitata pelaajaa sekä persoonapronominin ominaisuudella että pelaajan värillä: ::<code><span style="color: red">'''You'''</span> must choose a combination of dice</code> Tällä hetkellä tämä "you" on käännetty muotoon "sinä". Tämä ei sinällään ole huono vaihtoehto, ja tekstiä voidaan kääntää tämän mukaan. Ongelma on vain siinä, että useimmille kääntäjille ${you}-määritelmä jää pimentoon. Täten he saattavat kääntää mekaanisesti ylläolevan esimerkin muotoon: ::<code><span style="color: red">'''Sinä'''</span> täytyy valita noppien yhdistelmän</code> Jotkut ovat jopa "ratkaisseet" tämän lisäämällä määritelmän loppuun n-kirjaimen: ::<code><span style="color: red">'''Sinä'''</span>n täytyy valita noppien yhdistelmän</code> Yksi ratkaisu olisi kääntää tekstin äänensävy pakottavasta ("you must") mahdollistavaan ("you may"): ::<code><span style="color: red">'''Sinä'''</span> voit valita noppien yhdistelmän</code> Täten ongelmaa voidaan ratkaista kahdella tavalla: # Taivutetaan ${you} omistusmuotoon '''Sinun''' # Vaihdetaan käännettävän tekstin äänensävy Ensimmäisen vaihtoehdon ongelma on siinä, että se vie pohjan sen työn alta, joka on huomioinut määritelmän senhetkisen muodon. Toisen vaihtoehdon ongelma on sen kontekstiriippuvuus: voidaanko kaikkien tekstien äänensävyä vaihtaa ongelmitta? 93d16058e17990691161576b5d29017c5eb0712a 1152 1151 2020-05-29T08:31:26Z Rexroom 5688 /* ${you}-määritelmän joustamattomuus */ wikitext text/x-wiki Listaus ongelmista lokalisoinneissa. == Käännöstyöt == === ${you}-määritelmän joustamattomuus === BGA:ssa ${you}-funktiolla on tietty ominaisuus: sen tarkoituksena on tekstiosiossa viitata pelaajaa sekä persoonapronominin ominaisuudella että pelaajan värillä: ::<code><span style="color: red">'''You'''</span> must choose a combination of dice</code> Tällä hetkellä tämä "you" on käännetty muotoon "sinä". Tämä ei sinällään ole huono vaihtoehto, ja tekstiä voidaan kääntää tämän mukaan. Ongelma on vain siinä, että useimmille kääntäjille ${you}-määritelmä jää pimentoon. Täten he saattavat kääntää mekaanisesti ylläolevan esimerkin muotoon: ::<code><span style="color: red">'''Sinä'''</span> täytyy valita noppien yhdistelmän</code> Jotkut ovat jopa "ratkaisseet" tämän lisäämällä määritelmän loppuun n-kirjaimen: ::<code><span style="color: red">'''Sinä'''</span>n täytyy valita noppien yhdistelmän</code> Yksi ratkaisu olisi kääntää tekstin äänensävy pakottavasta ("you must") mahdollistavaan ("you may"): ::<code><span style="color: red">'''Sinä'''</span> voit valita noppien yhdistelmän</code> Täten ongelmaa voidaan ratkaista kahdella tavalla: # Taivutetaan ${you} omistusmuotoon '''Sinun''' # Vaihdetaan käännettävän tekstin äänensävy Ensimmäisen vaihtoehdon ongelma on siinä, että se vie pohjan sen työn alta, joka on huomioinut määritelmän senhetkisen muodon. Toisen vaihtoehdon ongelma on sen kontekstiriippuvuus: voidaanko kaikkien tekstien äänensävyä vaihtaa ongelmitta? Käytetäänkö muualla pelissä pakko/mahdollisuus-akselia? a64141a1ef3811a9702b4aa6f25200bd762a7430 1153 1152 2020-05-29T10:03:41Z Rexroom 5688 Lisää ajatuksia funktioista wikitext text/x-wiki Listaus ongelmista lokalisoinneissa. == Käännöstyöt == === ${you}-määritelmän joustamattomuus === BGA:ssa ${you}-funktiolla on tietty ominaisuus: sen tarkoituksena on tekstiosiossa viitata pelaajaa sekä persoonapronominin ominaisuudella että pelaajan värillä: ::<code><span style="color: red">'''You'''</span> must choose a combination of dice</code> Tällä hetkellä tämä "you" on käännetty muotoon "sinä". Tämä ei sinällään ole huono vaihtoehto, ja tekstiä voidaan kääntää tämän mukaan. Ongelma on vain siinä, että useimmille kääntäjille ${you}-määritelmä jää pimentoon. Täten he saattavat kääntää mekaanisesti ylläolevan esimerkin muotoon: ::<code><span style="color: red">'''Sinä'''</span> täytyy valita noppien yhdistelmä</code> Jotkut ovat jopa "ratkaisseet" tämän lisäämällä määritelmän loppuun n-kirjaimen: ::<code><span style="color: red">'''Sinä'''</span>n täytyy valita noppien yhdistelmä</code> Yksi ratkaisu olisi kääntää tekstin äänensävy pakottavasta ("you must") mahdollistavaan ("you may"): ::<code><span style="color: red">'''Sinä'''</span> voit valita noppien yhdistelmän</code> Toinen tapa olisi eristää funktio välimerkillä muusta lauseesta: ::<code><span style="color: red">'''Sinä'''</span>: Sinun täytyy valita noppien yhdistelmä</code> Täten ongelmaa voidaan ratkaista kolmella tavalla: # Taivutetaan ${you} omistusmuotoon "'''Sinun'''" # Vaihdetaan käännettävän tekstin äänensävy # Erotetaan ${you} muusta lauseesta välimerkillä Ensimmäisen vaihtoehdon ongelma on siinä, että se ei ratkaise alkuperäistä ongelmaa, eli sijamuodon epäsopivuutta lauseeseen. Uusi sijamuoto sopii hyvin eräisiin, mutta ei toisiin. Toisen vaihtoehdon ongelma on sen kontekstiriippuvuus: voidaanko kaikkien tekstien äänensävyä vaihtaa ongelmitta? Käytetäänkö muualla pelissä pakko/mahdollisuus-akselia? Kolmas vaihtoehto tuo esiin kieliopillisen ongelman. Kielitoimiston mukaan kaksoismerkkiä voidaan tulkita suoraa lainausta edeltävänä merkkinä. Pitäisikö tekstin sitten muuttaa repliikiksi, tyyliin "'''Minun''' täytyy valita noppien yhdistelmä"? Sopisiko jokin muu välimerkki paremmin? (Joillekin saattaa tulla ajatuksena, että voitaisiin jättää ${you}-funktio kokonaan pois. Heidän harmiksi sivusto ''vaatii'' että funktiot näkyvät myös käännetyissä teksteissä.) e3032bafdae7867f717e58b023e6e31978075e13 Ongelmakohdat 0 173 1154 1153 2020-05-29T19:04:23Z Rexroom 5688 Lisäajatuksia wikitext text/x-wiki Listaus ongelmista lokalisoinneissa. == Käännöstyöt == === ${you}-määritelmän joustamattomuus === BGA:ssa ${you}-funktiolla on tietty ominaisuus: sen tarkoituksena on tekstiosiossa viitata pelaajaa sekä persoonapronominin ominaisuudella että pelaajan värillä: ::<code><span style="color: red">'''You'''</span> must choose a combination of dice</code> Tällä hetkellä tämä "you" on käännetty muotoon "sinä". Tämä ei sinällään ole huono vaihtoehto, ja tekstiä voidaan kääntää tämän mukaan. Ongelma on vain siinä, että useimmille kääntäjille ${you}-määritelmä jää pimentoon. Täten he saattavat kääntää mekaanisesti ylläolevan esimerkin muotoon: ::<code><span style="color: red">'''Sinä'''</span> täytyy valita noppien yhdistelmä</code> Jotkut ovat jopa "ratkaisseet" tämän lisäämällä määritelmän loppuun n-kirjaimen: ::<code><span style="color: red">'''Sinä'''</span>n täytyy valita noppien yhdistelmä</code> Oikeampi tapa olisi käyttää sanaa "joutua": ::<code><span style="color: red">'''Sinä'''</span> joudut valitsemaan noppien yhdistelmän</code> Yksi ratkaisu olisi myös kääntää tekstin äänensävy pakottavasta ("you must") mahdollistavaan ("you may"): ::<code><span style="color: red">'''Sinä'''</span> voit valita noppien yhdistelmän</code> Toinen tapa olisi eristää funktio välimerkillä muusta lauseesta: ::<code><span style="color: red">'''Sinä'''</span>: Sinun täytyy valita noppien yhdistelmä</code> Täten ongelmaa voidaan ratkaista kolmella tavalla: # Taivutetaan ${you} omistusmuotoon "'''Sinun'''" # Vaihdetaan käännettävän tekstin äänensävy # Erotetaan ${you} muusta lauseesta välimerkillä Ensimmäisen vaihtoehdon ongelma on siinä, että se ei ratkaise alkuperäistä ongelmaa, eli sijamuodon epäsopivuutta lauseeseen. Uusi sijamuoto sopii hyvin eräisiin, mutta ei toisiin. Toisen vaihtoehdon ongelma on sen kontekstiriippuvuus: voidaanko kaikkien tekstien äänensävyä vaihtaa ongelmitta? Käytetäänkö muualla pelissä pakko/mahdollisuus-akselia? Kolmas vaihtoehto tuo esiin kieliopillisen ongelman. Kielitoimiston mukaan kaksoismerkkiä voidaan tulkita suoraa lainausta edeltävänä merkkinä. Pitäisikö tekstin sitten muuttaa repliikiksi, tyyliin "'''Minun''' täytyy valita noppien yhdistelmä"? Sopisiko jokin muu välimerkki paremmin? (Joillekin saattaa tulla ajatuksena, että voitaisiin jättää ${you}-funktio kokonaan pois. Heidän harmiksi sivusto ''vaatii'' että funktiot näkyvät myös käännetyissä teksteissä.) d92d490024b8059f3cc9c19eca5b8f1b3c4305e9 Ohje 0 167 1155 1149 2020-06-02T15:20:25Z Rexroom 5688 /* Kehityksessä */ 2 uutta peliä wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpfinity|Finity]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteatime|Teatime]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] edf486ea53af0f84f9013192d6097b52275dd2b9 1156 1155 2020-06-02T15:22:42Z Rexroom 5688 /* Kehityksessä */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpcoinche|Coinche]] * [[Gamehelpfinity|Finity]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] d27b594b396930645b50aa616f1504621018905b 1157 1156 2020-06-06T13:44:10Z Rexroom 5688 /* Peliohjeet */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbattleship|Battleship]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpbarbu|Barbu]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpfinity|Finity]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpwhisttwentytwo|Visti 22]] d072982586dd246bcda008c7f56bf99b8de0687a 1158 1157 2020-06-06T13:51:55Z Rexroom 5688 /* Pelattavana */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpbarbu|Barbu]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpfinity|Finity]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpwhisttwentytwo|Visti 22]] c75d5f69f080f33b2571d1bca41a5301cfa6c6b9 1162 1158 2020-06-08T12:02:01Z Rexroom 5688 /* Kehityksessä */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbombay|Bombay]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptroyes|Troyes]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] |} === Kehityksessä === * [[Gamehelpbarbu|Barbu]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpfinity|Finity]] * [[Gamehelpmarram|Marram]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpxanadu|Xanadu]] 952b5ed5a788a46962df86b9da4e9c7672aa94c8 1171 1162 2020-12-07T16:44:37Z Rexroom 5688 /* Peliohjeet */ Uusia lisäyksiä wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyokohama|Yokohama]] |} === Kehityksessä === * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpkami|Kami]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelppresident|Orja]] * [[Gamehelppedro|Pedro]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelproomtwentyfive|Room Twentyfive]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelphungariantarokk|Unkarilainen tarokki]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpxanadu|Xanadu]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] 00a13a8e2258392f054026e506ec794a35e7a761 1182 1171 2021-04-10T11:35:03Z Rexroom 5688 /* Peliohjeet */ Uudelleenjärjestelyä wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Kehityksessä === * [[Gamehelpabalone|Abalone]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpillustori|Illustori]] * [[Gamehelppresident|Orja]] * [[Gamehelppedro|Pedro]] * [[Gamehelproomtwentyfive|Room Twentyfive]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpyinyang|Yin Yang]] 7415bc84e7128f2cbc84bdbe5896b35e2ee7f7f2 1192 1182 2021-04-29T15:23:22Z Rexroom 5688 /* Peliohjeet */ Päivitys wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuilders|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Kehityksessä === * [[Gamehelpabalone|Abalone]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpiwari]] * [[Gamehelppresident|Orja]] * [[Gamehelppedro|Pedro]] * [[Gamehelproomtwentyfive|Room Twentyfive]] * [[Gamehelpyinyang|Yin Yang]] 9d72b06330da35e9166b5ff53c960096fed2b180 1193 1192 2021-04-29T15:24:45Z Rexroom 5688 /* Peliohjeet */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavana === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelppi|P.I.]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptiki|Tiki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Kehityksessä === * [[Gamehelpabalone|Abalone]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpiwari|Iwari]] * [[Gamehelppresident|Orja]] * [[Gamehelppedro|Pedro]] * [[Gamehelproomtwentyfive|Room Twentyfive]] * [[Gamehelpyinyang|Yin Yang]] 9ddd740f6aefd9724df387b311bfc610a582b002 1201 1193 2021-08-24T18:57:36Z Rexroom 5688 /* Peliohjeet */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] ' [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelproomtwentyfive|Room Twentyfive]] 9f223018d631bf63589ca9bc91923d937de6e050 1203 1201 2021-08-24T19:10:49Z Rexroom 5688 /* Pelattavia */ fix typo wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelproomtwentyfive|Room Twentyfive]] 9fa8924b46d7dc1d58688a674e68675eecc0fd83 Gamehelpchess 0 172 1159 1144 2020-06-06T14:04:05Z Rexroom 5688 Tekstin uudelleenjärjestely wikitext text/x-wiki Klassinen strateginen lautapeli kahdelle pelaajalle. == Päämäärä == Pelaajan on saatava vastustajansa kuningas tilanteeseen, jossa hänellä ei jää enää laillista siirtoa ilman, että hänen kuninkaansa syödään. == Nappulat == Nappulat eivät voi mennä muiden nappuloiden yli ellei toisin mainita. Vastustajan nappuloita "syödään" siirtämällä oma nappula samalle ruudulle kuin vastustajan, joka siirretään pois pelistä. === Kuningas === Liikkuu viistottain, vaaka- ja pystysuoraan 1 ruudun verran. === Kuningatar === Liikkuu viistottain, vaaka- ja pystysuoraan. === Torni === Liikkuu vaaka- ja pystysuoraan. === Lähetti === Liikkuu viistottain suoraan. === Ratsu === Liikkuu 2 ruutua vaaka- tai pystysuoraan suuntaan '''ja''' yhden ruudun sivulle. '''Voi hypätä nappuloiden yli.''' === Sotilas === Liikkuu eteenpäin alkurivistä joko 1 tai 2 ruutua, mutta sen jälkeen vain yhden ruudun. Liikkuu viistosti 1 ruudun, jos siellä on vastustajan nappula. == Erikoisliikkeet == === Linnoittautuminen === Kuningas voi vaihtaa jommankumman tornin kanssa paikkaa siirtymällä kaksi ruutua tornia kohti, jolloin torni siirtyy kuninkaan toiselle puolelle viereen. Liike on laillinen kun tietyt ehdot toteutuvat: * kuningas ja torni eivät ole liikkuneet vielä * kuningas, ylitettävä ruutu tai ruudut eikä siirtymäkohta ole uhattuna * kuninkaan ja tornin välillä ei ole muita nappuloita tiellä === Ohestalyönti === Jos vastustajan sotilas on siirtynyt 2 ruutua alkurivistään pelaajan sotilaan viereen, pelaaja voi tehdä ''ohestalyönnin'' ja viedä vastustajan sotilaan siirtämällä sotilaansa viistosti 1 ruudun alueelle niin kuin vastustaja olisi tehnyt vain 1 ruudun siirron. ''Tämä siirto on laillinen vain sen vuoron aikana.'' === Korotus === Sotilas, joka siirretään kauimpaan riviin, korotetaan samantien kuningattareksi, torniksi, lähetiksi tai ratsuksi. == Shakki ja matti == Shakki syntyy, kun vastapuolen nappula uhkaa kuningasta. ''Pelaajan on pakko saada kuningas pois tilanteesta.'' Matti syntyy, kun kuningasta ei saada pois shakista laillisin siirroin. === Tasapeli === Jos kumpikaan ei pysty tekemään mattia vastustajalleen erinäisistä syistä, pelaajat voivat sopia tasapelin. b7d3f6d2530207ab92b00364957a69fe617c2e0e Gamehelpbackgammon 0 174 1160 2020-06-06T17:05:33Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Klassinen 2 pelaajan lautapeli, joka on yhtä vanha kuin historia itse. == Päämäärä == Pelaajat yrittävät poistaa kaikki pelinappulansa laudalta. Siinä ensimmäisenä onnistunut on voittaja. == Pelin kulku == Aloittava pelaaja arvotaan. === Pelaajan vuoro === Pelaaja heittää kahta noppaa, jotka vastaavat pelaajan kahta siirtoa. Pelaajan täytyy siirtää noppien silmämäärän mukaisesti nappuloitaan laudalla. Esimerkiksi jos pelaaja heittää nopilla 2 ja 5, hän voi joko: * siirtää kaksi nappulaa: yhtä 2 askelta ja toista 5 tai * siirtää yhtä nappulaa 2 ja 5 askelta === Tuplat === Jos pelaaja heittää nopilla kahta samaa silmänlukua, hänellä on silloin neljä siirtoa kyseisen silmäluvun verran. Esim. 5-5 antaa pelaajan tehdä 4 siirtoa 5 askeleen verran. == Kiila == Kiila on pelissä yksi laudan paikoista, johon siirretään pelinappuloita. === Kiilan sulkemisesta === Jos kiilassa on vain yksi pelinappula, se on vaaranalainen: vastustaja voi lyödä sen suojamuuriin eli pelilautaa halkaisevaan alueeseen siirtämällä siihen oman pelinappulansa. Pelaaja voi sulkea kiilan sijoittamalla siihen toisen tai useamman pelinappulansa. Tällöin vastustaja ei pääse siirtymään kiilaan. === Suojamuurista siirtyminen === Jos pelaajalla on pelinappuloita suojamuurilla, hänen on siirrettävä ne vastustajan kotikentälle ennenkuin hän voi tehdä mitään muuta siirtoa. Voit käyttää vain yhtä noppaa muurissa olevalle pelinappulalle. Jos pelaaja ei pysty siirtämään pelinappulaansa muurista, kierros on ohi. == Puhdistaminen == Kun kaikki 15 pelinappulaasi ovat kotikentälläsi, voit aloittaa "puhdistamisen". Siirtämällä vähintään silmäluvun verran laudan ulkopuolelle, viet pelinappulasi laudalta ja täten turvaan. Samat siirtosäännöt pätevät, eli joudut tekemään mahdolliset siirrot. Voitat siirtämällä kaikki pelinappulasi laudasta. 937d6f81580793c36071e63b8ea7dd0634f7b2de 1161 1160 2020-06-06T18:16:14Z Rexroom 5688 /* Puhdistaminen */ Muutetaan kotiuttamiseksi wikitext text/x-wiki Klassinen 2 pelaajan lautapeli, joka on yhtä vanha kuin historia itse. == Päämäärä == Pelaajat yrittävät poistaa kaikki pelinappulansa laudalta. Siinä ensimmäisenä onnistunut on voittaja. == Pelin kulku == Aloittava pelaaja arvotaan. === Pelaajan vuoro === Pelaaja heittää kahta noppaa, jotka vastaavat pelaajan kahta siirtoa. Pelaajan täytyy siirtää noppien silmämäärän mukaisesti nappuloitaan laudalla. Esimerkiksi jos pelaaja heittää nopilla 2 ja 5, hän voi joko: * siirtää kaksi nappulaa: yhtä 2 askelta ja toista 5 tai * siirtää yhtä nappulaa 2 ja 5 askelta === Tuplat === Jos pelaaja heittää nopilla kahta samaa silmänlukua, hänellä on silloin neljä siirtoa kyseisen silmäluvun verran. Esim. 5-5 antaa pelaajan tehdä 4 siirtoa 5 askeleen verran. == Kiila == Kiila on pelissä yksi laudan paikoista, johon siirretään pelinappuloita. === Kiilan sulkemisesta === Jos kiilassa on vain yksi pelinappula, se on vaaranalainen: vastustaja voi lyödä sen suojamuuriin eli pelilautaa halkaisevaan alueeseen siirtämällä siihen oman pelinappulansa. Pelaaja voi sulkea kiilan sijoittamalla siihen toisen tai useamman pelinappulansa. Tällöin vastustaja ei pääse siirtymään kiilaan. === Suojamuurista siirtyminen === Jos pelaajalla on pelinappuloita suojamuurilla, hänen on siirrettävä ne vastustajan kotikentälle ennenkuin hän voi tehdä mitään muuta siirtoa. Voit käyttää vain yhtä noppaa muurissa olevalle pelinappulalle. Jos pelaaja ei pysty siirtämään pelinappulaansa muurista, kierros on ohi. == Kotiuttaminen == Kun kaikki 15 pelinappulaasi ovat kotikentälläsi, voit aloittaa "kotiuttamisen". Siirtämällä vähintään silmäluvun verran laudan ulkopuolelle, viet pelinappulasi laudalta ja täten turvaan. Samat siirtosäännöt pätevät, eli joudut tekemään mahdolliset siirrot. Voitat siirtämällä kaikki pelinappulasi laudasta. fce49963ca2ea890a13b74f5a542da02a03f58bd Sanakirja 0 171 1163 1145 2020-06-08T16:14:47Z Rexroom 5688 /* B */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;trick :tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 633bfccaf98e83d4732b06c69a7b9e9cf7874275 1168 1163 2020-12-06T17:12:11Z Rexroom 5688 /* T */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === ad0822ade4ca86440431b228f59371ffcccdc931 1169 1168 2020-12-06T18:02:55Z Rexroom 5688 /* Sanasto */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro) ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === fcbbb031a74f605c02bd038f1daa4587f61222b3 1172 1169 2020-12-09T18:58:03Z Rexroom 5688 /* P */ passaaminen wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 26cc255e0194e591b6723ccc5345173318fa78fa 1194 1172 2021-04-29T15:30:34Z Rexroom 5688 /* F */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;first player :''lautapeli'' aloituspelaaja ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 702b4012f42540fe47600a66e36239ebf2776b19 1196 1194 2021-05-07T17:42:10Z Rexroom 5688 /* A */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä ;action :toiminto, toiminta === B === ;bear off :''backgammon'' ulosheitto ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;first player :''lautapeli'' aloituspelaaja ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 98942428f2e0feed0b76c0f95ae910205d8a24ce Gamehelphanabi 0 175 1164 2020-08-21T16:20:53Z Seksiorja69 5750 Ak: Uusi sivu: Huono peli wikitext text/x-wiki Huono peli f49f2dc74f53195e78f42076ab79b24491b4ccb4 1166 1164 2020-11-09T15:41:41Z Pehepehe 5779 wikitext text/x-wiki Peli, jossa tehdään yhteistyötä parhaan mahdollisen tuloksen saamiseksi. Tässä pelissä ei kilpailla toisia vastaan. 0bfb72058f0eecc3d8e7ebd7a8692cf65947dc26 Gamehelpalhambra 0 176 1165 2020-11-03T16:19:59Z Rexroom 5688 Pikasäännöt wikitext text/x-wiki Voit tehdä yhden toiminnon vuorosi aikana: * Nostaa rahaa (joko yhden tai enintään 5 rahan edestä kortteja) * Ostaa rakennuslaatan * Järjestele alhambraasi uudestaan Jos ostat rakennuslaatan tasarahalla, saat suorittaa ylimääräisen toiminnon. Vuorosi lopussa aseta ostamasi rakennuslaatat alhambraasi. d429c783730bb81d3798ab184bf249892a4f8fde Gamehelpmartiandice 0 177 1167 2020-12-05T16:03:44Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Pelin päämäärä == Peliä pelataan kunnes joku saa yli 25 pistettä. Eniten pisteitä saanut voittaa. == Pelin kulku == Peli etenee myötäpäivää kierroksittain. === Pelaajan vuoro === # Pelaaja heittää kaikki vapaana olevat nopat. Jos vapaita noppia ei ole, siirry pisteytykseen. # Siirrä kaikki heitetyt tankit tankkipinoon. # Jäljellejääneistä nopista valitse yksi ryhmä nopan silmistä (kanat, lehmät, ihmiset tai kuolemansäteet). #* Voit valita vain kerran jokaisesta ryhmästä kierroksen aikana, poislukien kuolemansäteet. #* Jos et voi siirtää yhtään noppaa sivulle, siirry pisteytykseen. # Voit joko siirtyä pisteytykseen ja lopettaa kierroksesi tai aloittaa vuorosi alusta. === Kierroksen pisteytys === Jos tankkeja on enemmän kuin kuolemansäteitä, et saa tällä kierroksella pisteitä. Saat pisteen jokaisesta kaapatusta nopasta. Saat vielä 3 pistettä bonusta jos olet kaapannut 3 eri maan asukasta tällä kierroksella. Pisteytyksen lopussa kaikki nopat annetaan seuraavalle pelaajalle. === Pelin lopetus === Kun joku saa yli 25 pistettä, kierros pelataan loppuun. Tasapeliin päätyneet pelaajat heittävät 6 noppaa, eniten kuolemansäteitä saanut voittaa pelin. 0a24f41ad78a92a1c29f37b774268cd43dff7671 Gamehelpintheyearofthedragon 0 51 1170 558 2020-12-07T14:56:20Z Rexroom 5688 Suomennos. wikitext text/x-wiki === Kierroksen loppupisteytys === * 1 piste jokaisesta palatsista. * 1 piste jokaisesta hovineidosta. * 1 piste jokaisesta etuoikeudesta. (Pieni etuoikeus = 1 piste; Iso etuoikeus = 2 pistettä) === Pelin loppupisteytys === * 2 pistettä jokaisesta henkilölaatasta. * Jokaisesta munkista: pisteytä [Buddhien lukumäärä] x [kerrosten lukumäärä] pistettä. * Jokainen pelaaja myy takaisin varastoon jokaisesta riisi- ja ilotulitelaatasta 2 yuania. Sen jälkeen jokainen pelaaja saa 1 voittopistettä jokaisesta 3 yuanista mitä hänellä on. f3a25f3967d51407c339f619f4b441375588aac5 Gamehelpcarcassonne 0 178 1173 2020-12-28T23:21:04Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Päämäärä == Pelaajat asettelevat vuorottain pelialueelle laattoja, joka pelin edetessä muodostuu kaupunkien, luostareiden ja teiden verkostoksi. Pelaajat saavat pisteitä sen mukaan, kun saavat seuraajiensa pitämiä alueita valmiiksi. Pelaajien asetettua viimeisetkin laatat, peli loppuu ja lasketaan loputkin pisteistä. Pelaaja, joka on kerännyt eniten pisteitä lopussa, on voittaja. == Pelin kulku == Vuoronsa alussa pelaaja asettaa pinosta nostamansa laatan ja asettaa sen pelilaudalla siten, että se sopii kokonaisuuteen. Pelaaja voi sen jälkeen asettaa seuraajansa laattaan eri tarkoitusta varten, kuten maantierosvoksi teille, ritariksi kaupunkiin, munkiksi luostariin tai talonpojaksi pelloille. ''Huomaa, että et voi lisätä esim. samalle tielle toista seuraajaa.'' Alueen tulee olla vapaa muista seuraajista. Näiden vaiheiden jälkeen alkaa alueen mahdollinen pisteytys jos jokin alue saatiin valmiiksi. Sitten vuoro siirtyy seuraavalle pelaajalle. === Pisteytys === Kun pelaajat saavat jonkun alueen valmiiksi, alkaa pisteytys. Tie valmistuu, kun tien kummatkin päät loppuvat jonnekin. Pelaaja saa niin paljon pisteitä kuin tien pituus on laattoina. Kaupunki valmistuu, kun sitä ympäröivä muuri saadaan valmiiksi. Pelaaja saa 2 p. * kaupunkilaatta + 2 p. * vaakunakilpi. Luostari valmistuu, kun 8 sitä ympäröivää laattaa on sijoitettu. Pelaaja saa pisteen jokaisesta ympärillä olevasta laatasta ja itse luostarista. Jokaisesta valmistuneesta alueesta palautetaan seuraajat pelaajien varastoon uutta käyttöä varten. === Useita seuraajia samassa alueessa === Pelaajat eivät voi lisätä seuraajiaan alueelle jossa on jo yksi, mutta he voivat yhdistää eri alueet yhdeksi. Siten pisteytyksen yhteydessä jos jollakin pelaajalla on enemmän seuraajia vastavalmistuneella alueella, vain hän saa niistä pisteet. Tasatilanteissa alueen pisteet annetaan kaikille tasapeliin päätyneille. === Loppupisteytys === Loppupisteytys alkaa siinä vaiheessa, kun pelilaatat loppuvat kesken. Siinä vaiheessa keskeneräiset tiet, kaupungit ja luostarit pisteytetään. Pisteytys on sama kaupunkeja lukuunottamatta, jossa kaupunkilaatat ja vaakunakilvet ovat vain 1 pisteen arvoisia. Viimeiseksi lasketaan talonpoikien pisteet. Jokainen talonpojan asuttamaa peltoa koskettava valmis kaupunki tuottaa 3 pistettä. Eniten pisteitä kerännyt pelaaja on voittaja. 3de3bec8d62a458cf864705c55d7735543aa2873 Gamehelpkoikoi 0 179 1174 2021-01-16T22:26:48Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Koi-koi on japanilainen keräilypeli kahdelle pelaajalle, joka pelataan japanilaisilla [[wikipedia:fi:Hanafuda|Hanafuda]]-korteilla. == Hanafuda-pakka == Hanafudan 48 korttien kokonaisuudessa maat jakaantuvat 12 eri kuukauteen tai kasviin. Jokaisella kuukaudella on 1-3 eri hahmoa tai esinettä, jotka luokitellaan kortteina '''kirkkaiksi''', '''eläimiksi''' ja '''runoiksi'''. == Pelin kulku == Kyseessä on keräilypeliksi luokiteltava korttipeli, jossa yritetään rakennella eri Yaku-yhdistelmiä pisteiden toivossa. Pelin alussa pelaajille ja pelialueelle jaetaan 8 korttia. Pelaajat voivat pelata joko 6 tai 12 "kuukautta" (peliä). Jos pelaajalla on kädessään jaon jälkeen kuukauden kaikki neljä korttia, hän voittaa sen pelin ja saa 6 pistettä. Pelaaja pelaa yhden kortin kädestään. Jos pelattu kortti on paria pelialueen kortin maan kanssa, se pöydätään pelaajan eteen. Jos pelialueella on kolme yhden kuukauden korttia, neljäs pelattava kortti pöytää ne kaikki. Muussa tapauksessa pelattu kortti jätetään pelialueelle. Sen jälkeen nostetaan pakasta kortti, joka pelataan heti kuin se olisi pelaajan pelaama kortti. Sitten vuoro siirtyy vastustajalle. Pelaajan saadessaan valmiiksi jonkun Yakuista vuoronsa lopussa, pelaaja voi joko jatkaa peliä sanomalla "koi" tai lopettaa sen. Peli loppuu käsien tyhjennettyä. Jos kukaan ei ole saanut rakennettua yakua, jakaja saa 6 pistettä. == Yakut == Korteista saatavia yhdistelmiä voi katsoa pelin aikana "Voittamasi kortit" tekstilaatikon viereltä löytyvää ?-merkkiä napsauttamalla, joka listaa pelin korttiyhdistelmiä eri pisteytyksillä. Huomioi, että jotkut yakut ovat kasvavia, eli jos saat lisättyä yhden enemmän yhteen ryhmään, pistemääräsi kasvaa sen verran. == Muunnelmat == Pelistä on olemassa lukuisia muunnelmia, mutta BGA:n kannalta tärkeimmät ovat tässä: === Pehmeä Koi === Alussa pistekerroin on 1, mutta jos pelialueella jaon jälkeen näkyy kirkkaita, pistekerrointa nostetaan näkyvien kirkkaiden verran. Jos pelaaja sanoo "koi", ''hänen'' pistekerrointa nostetaan yhdellä. === Kova Koi === Jos pelaaja sanoo "koi", hänen ''vastustajansa'' tuplaa pisteensä jos hän onnistuu rakentamaan yakun. Jos pelaaja kerää 7 pistettä tai yli, hänen pisteensä tuplataan. Kummatkin tuplaukset pätevät. === "Katselemassa"-yakut === Tässä muunnelmassa voidaan pelata kahden kortin yakuja . '''Sade pilaa juhlat''' -muunnelmassa pöydätetyt marraskuun kortit estävät kahden kortin yakujen pisteytystä. c57cb883a5af450b93e164f6ecb946317a435387 1175 1174 2021-01-17T14:16:31Z Rexroom 5688 wikitext text/x-wiki Koi-koi on japanilainen keräilypeli kahdelle pelaajalle, joka pelataan japanilaisilla [[wikipedia:fi:Hanafuda|Hanafuda]]-korteilla. == Hanafuda-pakka == Hanafudan 48 korttien kokonaisuudessa maat jakaantuvat 12 eri kuukauteen tai kasviin. Jokaisella kuukaudella on 1-3 eri hahmoa tai esinettä, jotka luokitellaan kortteina '''kirkkaiksi''', '''eläimiksi''' ja '''runoiksi'''. == Pelin kulku == Kyseessä on keräilypeliksi luokiteltava korttipeli, jossa yritetään rakennella eri Yaku-yhdistelmiä pisteiden toivossa. Pelaajat voivat pelata joko 6 tai 12 "kuukautta" (peliä). Pelin alussa pelaajille ja pelialueelle jaetaan 8 korttia. Jäljellejäävät kortit asetetaan nostopakaksi pelialueen viereen. Kättä ei täydennetä uusilla korteilla pelin aikana. Jos pelaajalla on kädessään jaon jälkeen kuukauden kaikki neljä korttia, hän voittaa sen pelin ja saa 6 pistettä. Pelaaja pelaa yhden kortin kädestään. Jos pelattu kortti on paria pelialueen kortin maan kanssa, se pöydätään pelaajan eteen. Jos pelialueella on kolme yhden kuukauden korttia, neljäs pelattava kortti pöytää ne kaikki. Muussa tapauksessa pelattu kortti jätetään pelialueelle. Sen jälkeen nostetaan pakasta kortti, joka pelataan heti kuin se olisi pelaajan pelaama kortti. Sitten vuoro siirtyy vastustajalle. Pelaajan saadessaan valmiiksi jonkun Yakuista vuoronsa lopussa, pelaaja voi joko jatkaa peliä sanomalla "koi" tai lopettaa sen. Peli loppuu käsien tyhjennettyä. Jos kukaan ei ole saanut rakennettua yakua, jakaja saa 6 pistettä. == Yakut == Korteista saatavia yhdistelmiä voi katsoa pelin aikana "Voittamasi kortit" tekstilaatikon viereltä löytyvää ?-merkkiä napsauttamalla, joka listaa pelin korttiyhdistelmiä eri pisteytyksillä. Huomioi, että jotkut yakut ovat kasvavia, eli jos saat lisättyä yhden enemmän yhteen ryhmään, pistemääräsi kasvaa sen verran. == Muunnelmat == Pelistä on olemassa lukuisia muunnelmia, mutta BGA:n kannalta tärkeimmät ovat tässä: === Pehmeä Koi === Alussa pistekerroin on 1, mutta jos pelialueella jaon jälkeen näkyy kirkkaita, pistekerrointa nostetaan molemmille pelaajalle näkyvien kirkkaiden verran. Jos pelaaja sanoo "koi", ''hänen'' pistekerrointa nostetaan yhdellä. === Kova Koi === Jos pelaaja sanoo "koi", hänen ''vastustajansa'' tuplaa pisteensä jos hän onnistuu rakentamaan yakun. Jos pelaaja kerää 7 pistettä tai yli pelin aikana, hänen pisteensä tuplataan. Kummatkin tuplaukset pätevät, eli voit saada nelinkertaisen tuloksen. === "Katselemassa"-yakut === Tässä muunnelmassa voidaan pelata kahden kortin yakuja . '''Sade pilaa juhlat''' -muunnelmassa pöydätetyt marraskuun kortit estävät kahden kortin yakujen pisteytystä. 3e8a2ecd44601c2a4e68cb99444099ccadf8b2aa 1176 1175 2021-01-19T16:15:24Z Rexroom 5688 /* "Katselemassa"-yakut */ wikitext text/x-wiki Koi-koi on japanilainen keräilypeli kahdelle pelaajalle, joka pelataan japanilaisilla [[wikipedia:fi:Hanafuda|Hanafuda]]-korteilla. == Hanafuda-pakka == Hanafudan 48 korttien kokonaisuudessa maat jakaantuvat 12 eri kuukauteen tai kasviin. Jokaisella kuukaudella on 1-3 eri hahmoa tai esinettä, jotka luokitellaan kortteina '''kirkkaiksi''', '''eläimiksi''' ja '''runoiksi'''. == Pelin kulku == Kyseessä on keräilypeliksi luokiteltava korttipeli, jossa yritetään rakennella eri Yaku-yhdistelmiä pisteiden toivossa. Pelaajat voivat pelata joko 6 tai 12 "kuukautta" (peliä). Pelin alussa pelaajille ja pelialueelle jaetaan 8 korttia. Jäljellejäävät kortit asetetaan nostopakaksi pelialueen viereen. Kättä ei täydennetä uusilla korteilla pelin aikana. Jos pelaajalla on kädessään jaon jälkeen kuukauden kaikki neljä korttia, hän voittaa sen pelin ja saa 6 pistettä. Pelaaja pelaa yhden kortin kädestään. Jos pelattu kortti on paria pelialueen kortin maan kanssa, se pöydätään pelaajan eteen. Jos pelialueella on kolme yhden kuukauden korttia, neljäs pelattava kortti pöytää ne kaikki. Muussa tapauksessa pelattu kortti jätetään pelialueelle. Sen jälkeen nostetaan pakasta kortti, joka pelataan heti kuin se olisi pelaajan pelaama kortti. Sitten vuoro siirtyy vastustajalle. Pelaajan saadessaan valmiiksi jonkun Yakuista vuoronsa lopussa, pelaaja voi joko jatkaa peliä sanomalla "koi" tai lopettaa sen. Peli loppuu käsien tyhjennettyä. Jos kukaan ei ole saanut rakennettua yakua, jakaja saa 6 pistettä. == Yakut == Korteista saatavia yhdistelmiä voi katsoa pelin aikana "Voittamasi kortit" tekstilaatikon viereltä löytyvää ?-merkkiä napsauttamalla, joka listaa pelin korttiyhdistelmiä eri pisteytyksillä. Huomioi, että jotkut yakut ovat kasvavia, eli jos saat lisättyä yhden enemmän yhteen ryhmään, pistemääräsi kasvaa sen verran. == Muunnelmat == Pelistä on olemassa lukuisia muunnelmia, mutta BGA:n kannalta tärkeimmät ovat tässä: === Pehmeä Koi === Alussa pistekerroin on 1, mutta jos pelialueella jaon jälkeen näkyy kirkkaita, pistekerrointa nostetaan molemmille pelaajalle näkyvien kirkkaiden verran. Jos pelaaja sanoo "koi", ''hänen'' pistekerrointa nostetaan yhdellä. === Kova Koi === Jos pelaaja sanoo "koi", hänen ''vastustajansa'' tuplaa pisteensä jos hän onnistuu rakentamaan yakun. Jos pelaaja kerää 7 pistettä tai yli pelin aikana, hänen pisteensä tuplataan. Kummatkin tuplaukset pätevät, eli voit saada nelinkertaisen tuloksen. === "Katselemassa"-yakut === Tässä muunnelmassa voidaan pelata kahden kortin yakuja . '''Sade pilaa juhlat''' -muunnelmassa pöydätetty marraskuun salama-kortti estää kahden kortin yakujen pisteytykset. bed679fd0b7f88d0b699e5cd87f760fbc31c2c28 ServiceOutage 0 180 1177 2021-04-02T17:17:37Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki <div style="font-style: italic; border-radius: 20px; padding: 1.5em; border: 3px solid gray;"> === Filosofiamme === Pelipalvelun pyörittäminen 24 tuntia päivässä, 7 päivää viikossa ei ole helppoa näinkin (hyvin) pienellä joukolla. Sen lisäksi BGA on reaaliaikainen palvelu ja palvelukatkokset saattavat pilata meneillään olevia pelejä. Filosofiamme on olla suorasanainen tapahtuvista palvelukatkoksista kertomalla miksi se tapahtui, mihin se vaikutti (ja miten pitkään) ja mitä teimme, ettei se tapahdu uudestaan. Näiden selostaminen on tämän sivun tarkoitus. Huomio: Jos tapaus on meneillään, tilannepäivityksiä löytyy Twitter-tililtämme https://twitter.com/boardgamearena ja/tai Facebook-sivultamme https://www.facebook.com/boardgamearena/. </div> cbd868b676ef51d346a2c193006ed6fc0b960421 Tips luckynumbers 0 181 1178 2021-04-09T11:10:26Z 1ounz 5862 Ak: Uusi sivu: {{ •pelitapa: Tämä on onnenapilointiin peruatuva peli.__}} wikitext text/x-wiki {{ •pelitapa: Tämä on onnenapilointiin peruatuva peli.__}} ddabe134749f4f8b49eb89a46fd3e4ca393d3cc2 1179 1178 2021-04-09T11:16:37Z 1ounz 5862 Tämä on loistava kokonaisuus, josta kaikki oppivat varmasti toisilta jotain! wikitext text/x-wiki {{ •pelitapa: Tämä on onnenapilointiin peruantavapeli.__}} 93df1a99c8b1fd4acf7534be44a86085469229e2 Tips martiandice 0 182 1180 2021-04-09T22:46:38Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Perustaktiikka == Yritä saada jokaista kolmea maanasukas tyyppiä, koska saat niistä +3 lisäpistettä. Riippuen tilanteestasi, voit joko tehdä sen varmistamalla tai riskeeramalla. (Jos on käynnissä viimeinen kierros, on turha lähteä varmistelemaan jos olet reilusti häviöllä!) === Muista perääntyä === Joskus on viisainta olla heittämättä noppia. Erityisesti ''ei'' kannata jatkaa, jos sinulla on hyvä määrä pisteitä kasassa ja pystyt pakenemaan niiden kanssa! == Noppien heittojen todennäköisyyksistä == Nopissa on 5 erilaista puolta: Kuolemansäteitä (x2), tankkeja, ihmisiä, kanoja ja lehmiä. Voi siis sanoa, että jokaisella nopalla on 50% mahdollisuus saada maan asukas, 33% kuolonsäde ja 17% tankki. Alla on taulukko nopanheittojen todennäköisyyksistä: {| class="wikitable" !rowspan="2"|Noppia !colspan="11"|Tankkien/ihmisten/kanojen/lehmien määrien todennäköisyys |- !0 !1 !2 !3 !4 !5 !6 !7 !8 !9 |- !1 |83,33% |16,67% |- !2 |69,44% |27,78% |2,78% |- !3 |57,87% |34,72% |6,94% |0,46% |- !4 |48,23% |38,58% |11,57% |1,54% |0,08% |- !5 |40,19% |40,19% |16,08% |3,22% |0,32% |0,01% |- !6 |33,49% |40,19% |20,09% |5,36% |0,8% |0,06% |0,002% |- !7 |27,91% |39,07% |23,44% |7,81% |1,56% |0,19% |0,01% |0,0004% |- !8 |23,26% |37,21% |26,05% |10,42% |2,60% |0,42% |0,04% |0,002% |0,0001% |- !9 |19,38% |34,89% |27,91% |13,02% |3,91% |0,78% |0,1% |0,01% |0,0004% |0,00001% |- !10 |16,15% |32,3% |29,07% |15,5% |5,43% |1,3% |0,22% |0,02% |0,002% |0,0001% |- !11 |13,46% |29,61% |29,61% |17,77% |7,11% |1,99% |0,4% |0,06% |0,01% |0,0004% |- !12 |11,22% |26,92% |29,61% |19,74% |8,88% |2,84% |0,66% |0,11% |0,01% |0,001% |- !13 |9,35% |24,3% |29,16% |21,38% |10,69% |3,85% |1,03% |0,21% |0,03% |0,003% |} {| class="wikitable" !rowspan="2"|Noppia !colspan="11"|Kuolonsäteiden määrien todennäköisyys |- !0 !1 !2 !3 !4 !5 !6 !7 !8 !9 |- !1 |66,67% |33,33% |- !2 |44,44% |44,44% |11,11% |- !3 |29,63% |44,44% |22,22% |3,7% |- !4 |19,75% |39,51% |29,63% |9,88% |1,23% |- !5 |13,17% |32,92% |32,92% |16,46% |4,12% |0,41% |- !6 |8,78% |26,34% |32,92% |21,95% |8,23% |1,65% |0,14% |- !7 |5,85% |20,48% |30,73% |25,61% |12,8% |3,84% |0,64% |0,05% |- !8 |3,9% |15,61% |27,31% |27,31% |17,07% |6,83% |1,71% |0,24% |0,02% |- !9 |2,6% |11,71% |23,41% |27,31% |20,48% |10,24% |3,41% |0,73% |0,09% |0,01% |- !10 |1,73% |8,67% |19,51% |26,01% |22,76% |13,66% |5,69% |1,63% |0,3% |0,03% |- !11 |1,16% |6,36% |15,9% |23,84% |23,84% |16,69% |8,35% |2,98% |0,75% |0,12% |- !12 |0,77% |4,62% |12,72% |21,2% |23,84% |19,08% |11,13% |4,77% |1,49% |0,33% |- !13 |0,51% |3,34% |10,02% |18,37% |22,96% |20,67% |13,78% |6,89% |2,58% |0,72% |} 41687ce6809426831cca4ae60fd9c91d16116fbc 1181 1180 2021-04-09T23:24:13Z Rexroom 5688 /* Noppien heittojen todennäköisyyksistä */ wikitext text/x-wiki == Perustaktiikka == Yritä saada jokaista kolmea maanasukas tyyppiä, koska saat niistä +3 lisäpistettä. Riippuen tilanteestasi, voit joko tehdä sen varmistamalla tai riskeeramalla. (Jos on käynnissä viimeinen kierros, on turha lähteä varmistelemaan jos olet reilusti häviöllä!) === Muista perääntyä === Joskus on viisainta olla heittämättä noppia. Erityisesti ''ei'' kannata jatkaa, jos sinulla on hyvä määrä pisteitä kasassa ja pystyt pakenemaan niiden kanssa! == Noppien heittojen todennäköisyyksistä == Nopissa on 5 erilaista puolta: Kuolemansäteitä (x2), tankkeja, ihmisiä, kanoja ja lehmiä. Voi siis sanoa, että jokaisella nopalla on 50% mahdollisuus saada maan asukas, 33% kuolonsäde ja 17% tankki. Alla on taulukko nopanheittojen todennäköisyyksistä: {| class="wikitable" !rowspan="2"|Noppia !colspan="11"|Tankkien/ihmisten/kanojen/lehmien määrien todennäköisyys |- !0 !1 !2 !3 !4 !5 !6 !7 !8 !9 |- !1 |83,33% |16,67% |- !2 |69,44% |27,78% |2,78% |- !3 |57,87% |34,72% |6,94% |0,46% |- !4 |48,23% |38,58% |11,57% |1,54% |0,08% |- !5 |40,19% |40,19% |16,08% |3,22% |0,32% |0,01% |- !6 |33,49% |40,19% |20,09% |5,36% |0,8% |0,06% |0,002% |- !7 |27,91% |39,07% |23,44% |7,81% |1,56% |0,19% |0,01% |0,0004% |- !8 |23,26% |37,21% |26,05% |10,42% |2,60% |0,42% |0,04% |0,002% |0,0001% |- !9 |19,38% |34,89% |27,91% |13,02% |3,91% |0,78% |0,1% |0,01% |0,0004% |0,00001% |- !10 |16,15% |32,3% |29,07% |15,5% |5,43% |1,3% |0,22% |0,02% |0,002% |0,0001% |- !11 |13,46% |29,61% |29,61% |17,77% |7,11% |1,99% |0,4% |0,06% |0,01% |0,0004% |- !12 |11,22% |26,92% |29,61% |19,74% |8,88% |2,84% |0,66% |0,11% |0,01% |0,001% |- !13 |9,35% |24,3% |29,16% |21,38% |10,69% |3,85% |1,03% |0,21% |0,03% |0,003% |} {| class="wikitable" !rowspan="2"|Noppia !colspan="11"|Kuolonsäteiden määrien todennäköisyys |- !0 !1 !2 !3 !4 !5 !6 !7 !8 !9 |- !1 |66,67% |33,33% |- !2 |44,44% |44,44% |11,11% |- !3 |29,63% |44,44% |22,22% |3,7% |- !4 |19,75% |39,51% |29,63% |9,88% |1,23% |- !5 |13,17% |32,92% |32,92% |16,46% |4,12% |0,41% |- !6 |8,78% |26,34% |32,92% |21,95% |8,23% |1,65% |0,14% |- !7 |5,85% |20,48% |30,73% |25,61% |12,8% |3,84% |0,64% |0,05% |- !8 |3,9% |15,61% |27,31% |27,31% |17,07% |6,83% |1,71% |0,24% |0,02% |- !9 |2,6% |11,71% |23,41% |27,31% |20,48% |10,24% |3,41% |0,73% |0,09% |0,01% |- !10 |1,73% |8,67% |19,51% |26,01% |22,76% |13,66% |5,69% |1,63% |0,3% |0,03% |- !11 |1,16% |6,36% |15,9% |23,84% |23,84% |16,69% |8,35% |2,98% |0,75% |0,12% |- !12 |0,77% |4,62% |12,72% |21,2% |23,84% |19,08% |11,13% |4,77% |1,49% |0,33% |- !13 |0,51% |3,34% |10,02% |18,37% |22,96% |20,67% |13,78% |6,89% |2,58% |0,72% |} Täten taulukoiden mukaan aloitusheitossa on todennäköisemmin 4 kuolonsädettä, 2 tankkia, 2 ihmistä, 2 lehmää ja 2 kanaa. 6e65c9df46323e764eb1110c0e125809a24a1727 Gamehelpabalone 0 183 1183 2021-04-10T14:04:54Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Pelin tavoite == Ole ensimmäinen joka saa tönittyä 6 vastustajan kuulaa laudalta. == Asettelut == Voit tällä hetkellä valita kolmesta eri alkuasettelua BGA:lla. === Perusalkuasettelu === Kuulat asetellaan laudan eri päihin kolmeen riviin. [[File:Abalone standard.svg|thumb|Peruspeli]] === Belgialainen päivänkakkara === Kuulat asetellaan 7 kuulan ryhmiin. Ryhmät sijoitetaan laudan päihin pareiksi, lähellä toisiaan. [[File:Abalone belgian.svg|thumb|Belgialainen päivänkakkara]] === Saksalainen päivänkakkara === Sama kuin edellinen, mutta niin että ryhmien välillä on yhden ruudun välit. [[File:Abalone german.svg|thumb|Saksalainen päivänkakkara]] == Kuulien siirtely == Vuorosi aika voit siirtää kuuliasi ''yhden ruudun'' kahdella tapaa, ''linjasiirolla'' tai ''sivusiirrolla''. Linjasiirrolla voit siirtää 1-3 kuulaa yhtä linjaa pitkin. Sivusiirrolla voit siirtää 2-3 kuulaa sivulle. ''Sivusiirrolla ei voi töniä kuulia.'' ''Voit siirtää kuuliasi vain yhteen suuntaan vuorosi aikana.'' == Kuulien työntämisestä == Voit työntää vastustajan kuulia jos työntävä linja on isompi kuin vastapuolen. Esimerkiksi 3 kuulan linja voi työntää vastapuolen 1-2 kuulan linjaa. Huom! Et voi töniä omaa kuulaasi. Tämä pätee myös silloin, jos vastustajan kuulien takana on oma kuulasi. 97582eab2071d00242ad9998e8afe27ad6040706 1184 1183 2021-04-10T15:14:27Z Rexroom 5688 /* Asettelut */ wikitext text/x-wiki == Pelin tavoite == Ole ensimmäinen joka saa tönittyä 6 vastustajan kuulaa laudalta. == Asettelut == Voit tällä hetkellä valita kolmesta eri alkuasettelua BGA:lla. === Perusalkuasettelu === Kuulat asetellaan laudan eri päihin kolmeen riviin. [[File:Abalone standard.png|thumb|Peruspeli]] === Belgialainen päivänkakkara === Kuulat asetellaan 7 kuulan ryhmiin. Ryhmät sijoitetaan laudan päihin pareiksi, lähellä toisiaan. [[File:Abalone belgian.png|thumb|Belgialainen päivänkakkara]] === Saksalainen päivänkakkara === Sama kuin edellinen, mutta niin että ryhmien välillä on yhden ruudun välit. [[File:Abalone german.png|thumb|Saksalainen päivänkakkara]] == Kuulien siirtely == Vuorosi aika voit siirtää kuuliasi ''yhden ruudun'' kahdella tapaa, ''linjasiirolla'' tai ''sivusiirrolla''. Linjasiirrolla voit siirtää 1-3 kuulaa yhtä linjaa pitkin. Sivusiirrolla voit siirtää 2-3 kuulaa sivulle. ''Sivusiirrolla ei voi töniä kuulia.'' ''Voit siirtää kuuliasi vain yhteen suuntaan vuorosi aikana.'' == Kuulien työntämisestä == Voit työntää vastustajan kuulia jos työntävä linja on isompi kuin vastapuolen. Esimerkiksi 3 kuulan linja voi työntää vastapuolen 1-2 kuulan linjaa. Huom! Et voi töniä omaa kuulaasi. Tämä pätee myös silloin, jos vastustajan kuulien takana on oma kuulasi. 7dcefc86e3b7dc09564dc23647087be0158de30e 1188 1184 2021-04-10T15:51:17Z Rexroom 5688 /* Asettelut */ wikitext text/x-wiki == Pelin tavoite == Ole ensimmäinen joka saa tönittyä 6 vastustajan kuulaa laudalta. == Alkuasetteluja == Voit tällä hetkellä valita kolmesta eri alkuasettelua BGA:lla. === Perus === [[File:Abalone standard.png|100px|Peruspeli]] Kuulat asetellaan laudan eri päihin kolmeen riviin. === Belgialainen päivänkakkara === [[File:Abalone belgian.png|100px|Belgialainen päivänkakkara]] Kuulat asetellaan 7 kuulan ryhmiin. Ryhmät sijoitetaan pelilaudan päihin pareiksi. === Saksalainen päivänkakkara === [[File:Abalone german.png|100px|Saksalainen päivänkakkara]] Sama kuin edellinen, mutta yhtä lähellä toisiaan. == Kuulien siirtely == Vuorosi aika voit siirtää kuuliasi ''yhden ruudun'' kahdella tapaa, ''linjasiirolla'' tai ''sivusiirrolla''. Linjasiirrolla voit siirtää 1-3 kuulaa yhtä linjaa pitkin. Sivusiirrolla voit siirtää 2-3 kuulaa sivulle. ''Sivusiirrolla ei voi töniä kuulia.'' ''Voit siirtää kuuliasi vain yhteen suuntaan vuorosi aikana.'' == Kuulien työntämisestä == Voit työntää vastustajan kuulia jos työntävä linja on isompi kuin vastapuolen. Esimerkiksi 3 kuulan linja voi työntää vastapuolen 1-2 kuulan linjaa. Huom! Et voi töniä omaa kuulaasi. Tämä pätee myös silloin, jos vastustajan kuulien takana on oma kuulasi. d0d4be2bc2f83a6c3af5837da839d9647cc1e02f 1189 1188 2021-04-10T16:04:48Z Rexroom 5688 /* Kuulien siirtely */ wikitext text/x-wiki == Pelin tavoite == Ole ensimmäinen joka saa tönittyä 6 vastustajan kuulaa laudalta. == Alkuasetteluja == Voit tällä hetkellä valita kolmesta eri alkuasettelua BGA:lla. === Perus === [[File:Abalone standard.png|100px|Peruspeli]] Kuulat asetellaan laudan eri päihin kolmeen riviin. === Belgialainen päivänkakkara === [[File:Abalone belgian.png|100px|Belgialainen päivänkakkara]] Kuulat asetellaan 7 kuulan ryhmiin. Ryhmät sijoitetaan pelilaudan päihin pareiksi. === Saksalainen päivänkakkara === [[File:Abalone german.png|100px|Saksalainen päivänkakkara]] Sama kuin edellinen, mutta yhtä lähellä toisiaan. == Kuulien siirtely == Vuorosi aikana voit siirtää kuuliasi ''yhden ruudun'' kahdella tapaa, ''linjasiirolla'' tai ''sivusiirrolla''. Voit siirtää yksittäistä kuulaa viereiseen vapaaseen paikkaan. Linjasiirrolla voit siirtää 2-3 kuulaa eteen- tai taaksepäin. Sivusiirrolla voit siirtää 2-3 kuulaa sivulle. ''Sivusiirrolla ei voi töniä kuulia.'' Siirrettävä kuularyhmä ''pitää olla'' yhtenäinen suora linja. ''Voit siirtää kuuliasi vain samaan suuntaan vuorollasi.'' == Kuulien työntämisestä == Voit työntää vastustajan kuulia jos työntävä linja on isompi kuin vastapuolen. Esimerkiksi 3 kuulan linja voi työntää vastapuolen 1-2 kuulan linjaa. Huom! Et voi töniä omaa kuulaasi. Tämä pätee myös silloin, jos vastustajan kuulien takana on oma kuulasi. 50518d6ce83629ae1695936259c31f57cc65848f Tiedosto:Abalone standard.png 6 184 1185 2021-04-10T15:16:03Z Rexroom 5688 By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484128 wikitext text/x-wiki == Yhteenveto == By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484128 b845c5337b342e489056ef5e0fc5abce5805c1c9 Tiedosto:Abalone belgian.png 6 185 1186 2021-04-10T15:38:42Z Rexroom 5688 Abalonen belgialainen päivänkakkara -asetelma By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484039 wikitext text/x-wiki == Yhteenveto == Abalonen belgialainen päivänkakkara -asetelma By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484039 46e82e84bc66d35f64b7ddc19d3cb1f2ad385732 Tiedosto:Abalone german.png 6 186 1187 2021-04-10T15:44:19Z Rexroom 5688 Abalonen saksalainen päivänkakkara -alkuasetelma By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484088 wikitext text/x-wiki == Yhteenveto == Abalonen saksalainen päivänkakkara -alkuasetelma By François Haffner - réalisé avec Inkscape, Public Domain, https://commons.wikimedia.org/w/index.php?curid=1484088 ed3c7e635c6db052d20de5beab188ce3774401fb Gamehelploveletter 0 187 1190 2021-04-25T12:05:02Z Helmi123 5880 Ak: Uusi sivu: Peli Yksi 16 kortista otetaan pelin alussa pois pakasta ja asetetaan sivuun. Muut kortit sekoitetaan. Jokainen pelaaja saa 1 kortin ja muut kortit laitetaan pakaksi pöydän keske... wikitext text/x-wiki Peli Yksi 16 kortista otetaan pelin alussa pois pakasta ja asetetaan sivuun. Muut kortit sekoitetaan. Jokainen pelaaja saa 1 kortin ja muut kortit laitetaan pakaksi pöydän keskelle. Pelaajat vuorotellen nostavat kortin (jolloin kädessä on 2 korttia) ja asettavat toisen kortin pöydälle (jolloin käteen jää 1 kortti). Kun asetat kortin pöydälle, pelaat sen vaikutuksen. Kierros päättyy kun pakassa ei ole enää jäljellä kortteja joita nostaa tai kun kaikki muut pelaajat ovat tippuneet kierrokselta. Pelaaja jolla on kädessään korkein kortti saa 1 merkin. Jos tulos on tasapeli se ratkeaa sen perusteella kenellä on edessään korkeampi summa kortteja. Jos tulos on yhä tasapeli, kumpikaan ei saa merkkiä. Pelaaja voittaa kun hänen on voittanut tietyn määrän merkkejä. Kuinka monta merkkiä tulee kerätä riippuu pelaajien määrästä: 2 pelaajaa: 7 merkkiä (ei pysty pelaamaan vielä BGA:aassa) 3 pelaajaa: 5 merkkiä 4 pelaajaa: 4 merkkiä 5-8 pelaajaa: 4 merkkiä 485ed5f5ad325b837b3255bb13253357ba6f096a 1202 1190 2021-08-24T19:04:56Z Rexroom 5688 wikitext text/x-wiki == Peli == Yksi kortti otetaan pelin alussa pois 16 kortin pakasta ja asetetaan sivuun. Muut kortit sekoitetaan. Jokainen pelaaja saa 1 kortin ja muut kortit laitetaan pakaksi pöydän keskelle. Pelaajat vuorotellen nostavat kortin (jolloin kädessä on 2 korttia) ja asettavat toisen kortin pöydälle (jolloin käteen jää 1 kortti). Kun asetat kortin pöydälle, pelaat sen vaikutuksen. Kierros päättyy kun pakassa ei ole enää jäljellä kortteja, joita nostaa tai kun kaikki muut pelaajat ovat tippuneet kierrokselta. Pelaaja jolla on kädessään korkein kortti saa 1 merkin. Jos tulos on tasapeli se ratkeaa sen perusteella kenellä on edessään korkeampi summa kortteja. Jos tulos on yhä tasapeli, kumpikaan ei saa merkkiä. Pelaaja voittaa kun hänen on voittanut tietyn määrän merkkejä. Kuinka monta merkkiä tulee kerätä riippuu pelaajien määrästä: * 2 pelaajaa: 7 merkkiä (ei pysty pelaamaan vielä BGA:ssa) * 3 pelaajaa: 5 merkkiä * 4 pelaajaa: 4 merkkiä * 5-8 pelaajaa: 4 merkkiä 42fd45cf8f525e8dc0bc36dc0363765c6c27cc86 Gamehelpsplits 0 188 1191 2021-04-28T15:10:55Z Tinttiboii 5882 cool wikitext text/x-wiki yo c41975d1dae1cc69b16ad8892b8c77164e84ca39 Gamehelpthurnandtaxis 0 189 1195 2021-05-02T17:55:12Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki '''Thurn und Taxis''' on 2-4 pelaajan peli, tarkoituksena rakentaa kattavin postipalveluyritys. Se onnistuu rakentamalla postipalveluverkoston, poimien pisteitä bonuslaatoista ja vankkureista. Peli päättyy kierrokseen, jossa joku pelaajista saa kaikki konttorinsa rakennettua tai saa 7 pituisen reitin vaunun. == Pelaajan vuoro == Pelaajan vuoro menee ''järjestyksessä'' seuraavasti: # Pelaaja ''nostaa'' kaupunkikortin # Pelaaja ''pöytää'' kaupunkikortin # Pelaaja voi ''pisteyttää'' reittinsä Pelaajalla on myös käytössään 4 virkamiestä: postimestari, kirjanpitäjä, postiljooni ja varikkopäällikkö. Pelaaja voi vuoronsa aikana hyödyntää ''yhden'' virkamiehen erikoisominaisuuden vuoronsa aikana. === Nostovaihe === Pelaaja nostaa yhden kaupunkikortin joko pelilaudalta tai sokkona pakasta. Pelilaudasta otettu kortti korvataan uudella kortilla pakasta. === Pöytäysvaihe === Pelaaja pöytää kortin eteensä joko uudeksi reitiksi tai jatkaakseen vanhaa reittiään. Reittiä voi jatkaa kummastakin päästä, mutta ei muualta. Muuten aloitat kokonaan uuden reitin ja menetät mahdollisuuden pisteyttää reittiäsi. === Pisteytysvaihe === Voit pisteyttää reittisi sijoittamalla konttorisi reitin varrelle. Pisteyttäminen poistaa nykyisen reittisi. Voit sijoittaa konttoreita kahdella tapaa: # Konttoreita yhdelle maalle # Yksi konttori per reitin käymä maa Jos saat yhden maan tai maaliiton täyteen konttoreita, ota päällimmäisin sitä vastaava bonuslaatta kokoelmiisi. Samoin 5, 6, tai 7 pituisten reittien kohdalla ota sitä vastaava bonuslaatta. === Virkamiehet === Nostovaiheen aikana: * Postimestarilla voit nostaa toisenkin kaupunkikortin vuorosi aikana (pakollinen ensimmäisellä kierroksella) * Kirjanpitäjä vaihtaa esillä olevat 6 kaupunkikorttia uuteen erään Pöytäysvaiheen aikana: * Postiljoonilla voit lisätä vielä yhden kaupunkikortin reittiisi Pisteytysvaiheen aikana: * Varikkopäälliköllä voit ottaa seuraavan vaunukortin, vaikka reittisi olisi yhden tai kahden kortin verran lyhyempi == Pelin päättyminen == Kun joku pelaajista saa 7-vaunukortin tai saa sijoitettua kaikki konttorinsa laudalle, hän ottaa pelin lopetus bonuslaatan. Pelatkaa kierros loppuun, jolloin peli päättyy. a1143c5e7ccaba53f1be9e51c2abe5f5d69bbc3c Gamehelpwizard 0 190 1197 2021-06-18T20:03:55Z Sarkkukukka 5888 Sisältö 1 Tavoite 2 Pelin kulku 3 Pisteytys 4 Pelin päätös 5 Variantit 5.1 Vedonlyönti 5.2 Tavoite wikitext text/x-wiki TAVOITE Sinun pitää arvata oikein voittamiesi tikkien määrä joka kierroksella saadaksesi mahdollisimman paljon pisteitä. Eniten pisteitä kerännyt voittaa! PELIN KULKU Sekoita kaikki 60 korttia, sisältäen 13 kääpiötä, 13 haltiaa, 13 jättiläistä, 13 ihmistä, 4 velhoa ja 4 narria. Jaa jokaiselle pelaajalle kierrosluvun verran kortteja. Seuraavaksi oikein päin käännetty kortti on valtti. Jos kortti on narri, kierroksella ei ole valttia. Jos kortti on velho, jakaja valitsee valtin. Viimeisellä kierroksella ei ole valttia. Jokainen pelaaja, aloittaen jakajan vasemmalla olevasta pelaajasta, arvaa kuinka monta tikkiä hän voittaa kierroksella. Jakajan vasemmalla puolella oleva pelaaja aloittaa tikin millä tahansa kortilla. Jos aloittava kortti on velho, se voittaa välttämättä. Jos kortti on narri, seuraava pelaaja päättää tikin värin. Jos tikissä pelataan vain narreja, ensimmäinen narri voittaa. Pelaajien täytyy seurata tikin väriä, jos mahdollista. Jos pelaaja ei siihen pysty, hän voi pelata toisen värin kortin, mukaan lukien valttiväriä. Velhoja ja narreja voi pelata milloin vain. Ensimmäinen pelattu velho voittaa tikin. Jos tikissä ei ole velhoja, sen voittaa korkein valttivärin kortti. Muussa tapauksessa sen voittaa korkein tikkivärin kortti. Tikin voittaja aloittaa seuraavan tikin. PISTEYTYS Jos pelaaja arvasi oikein tikkiensä määrän kierroksen alussa, he saavat 20 pistettä sekä 10 pistettä jokaista voitettua tikkiä kohden. Jos pelaaja arvasi väärin tikkiensä määrän, he menettävät 10 pistettä jokaista väärin arvattua tikkiä kohden. PELIN PÄÄTÖS Viimeinen kierros koittaa, kun kaikki kortit ovat jaettu. Kolmella pelaajalla pelataan näin 20 kierrosta, neljällä pelaajalla 15 kierrosta, viidellä 12 ja kuudella 6. Pelin lopussa eniten pisteitä kerännyt voittaa. VARIANTIT Arvaaminen Piilotettu arvaus: arvaukset paljastetaan samaan aikaan ennen kuin kierros aloitetaan. Plus miinus 1: arvattujen tikkien määrä kierroksella ei saa olla sama kuin kierroksen tikkien määrä. Salainen arvaus: arvaukset paljastetaan vasta kierroksen päättyessä. Pelin pituus 5 kierrosta: kaikki kortit jaetaan joka kierroksella. Peliä pelataan vain viisi kierrosta. Amigo-turnaussäännöt: Peliä pelataan 10 kierrosta tietyllä määrällä kortteja. Kolmen pelaajan pelissä, jokainen aloittaa kahdella kortilla ja korttimäärä kasvaa kahdella joka kierroksella. Neljän pelaajan pelissä aloitetaan yhdellä kortilla, ja korttimäärää kasvatetaan kahdella joka kierros kuudenteen kierrokseen asti, jonka jälkeen korttimäärää kasvaa vain yhdellä kortilla joka kierros. Viiden pelaajan pelissä aloitetaan kahdella kortilla, ja korttimäärää kasvatetaan kahdella toisella kierroksella ja yhdellä muilla kierroksilla. 6d36fe25ec0796dd3cde26b04b202e5e4a67a84d 1198 1197 2021-06-20T19:03:17Z Sarkkukukka 5888 wikitext text/x-wiki TAVOITE Sinun pitää arvata oikein voittamiesi tikkien määrä joka kierroksella saadaksesi mahdollisimman paljon pisteitä. Eniten pisteitä kerännyt voittaa! PELIN KULKU Sekoita kaikki 60 korttia, sisältäen 13 kääpiötä, 13 haltiaa, 13 jättiläistä, 13 ihmistä, 4 velhoa ja 4 narria. Jaa jokaiselle pelaajalle kierrosluvun verran kortteja. Seuraavaksi oikein päin käännetty kortti on valtti. Jos kortti on narri, kierroksella ei ole valttia. Jos kortti on velho, jakaja valitsee valtin. Viimeisellä kierroksella ei ole valttia. Jokainen pelaaja, aloittaen jakajan vasemmalla olevasta pelaajasta, arvaa kuinka monta tikkiä hän voittaa kierroksella. Jakajan vasemmalla puolella oleva pelaaja aloittaa tikin millä tahansa kortilla. Jos aloittava kortti on velho, se voittaa välttämättä. Jos kortti on narri, seuraava pelaaja päättää tikin värin. Jos tikissä pelataan vain narreja, ensimmäinen narri voittaa. Pelaajien täytyy seurata tikin väriä, jos mahdollista. Jos pelaaja ei siihen pysty, hän voi pelata toisen värin kortin, mukaan lukien valttiväriä. Velhoja ja narreja voi pelata milloin vain. Ensimmäinen pelattu velho voittaa tikin. Jos tikissä ei ole velhoja, sen voittaa korkein valttivärin kortti. Muussa tapauksessa sen voittaa korkein tikkivärin kortti. Tikin voittaja aloittaa seuraavan tikin. PISTEYTYS Jos pelaaja arvasi oikein tikkiensä määrän kierroksen alussa, he saavat 20 pistettä sekä 10 pistettä jokaista voitettua tikkiä kohden. Jos pelaaja arvasi väärin tikkiensä määrän, he menettävät 10 pistettä jokaista väärin arvattua tikkiä kohden. PELIN PÄÄTÖS Viimeinen kierros koittaa, kun kaikki kortit ovat jaettu. Kolmella pelaajalla pelataan näin 20 kierrosta, neljällä pelaajalla 15 kierrosta, viidellä 12 ja kuudella 6. Pelin lopussa eniten pisteitä kerännyt voittaa. VARIANTIT Arvaaminen Piilotettu arvaus: arvaukset paljastetaan samaan aikaan ennen kuin kierros aloitetaan. Plus miinus 1: arvattujen tikkien määrä kierroksella ei saa olla sama kuin kierroksen tikkien määrä. Salainen arvaus: arvaukset paljastetaan vasta kierroksen päättyessä. Pelin pituus 5 kierrosta: kaikki kortit jaetaan joka kierroksella. Peliä pelataan vain viisi kierrosta. Amigo-turnaussäännöt: Peliä pelataan 10 kierrosta tietyllä määrällä kortteja. Kolmen pelaajan pelissä, jokainen aloittaa kahdella kortilla ja korttimäärä kasvaa kahdella joka kierroksella. Neljän pelaajan pelissä aloitetaan yhdellä kortilla, ja korttimäärää kasvatetaan kahdella joka kierros kuudenteen kierrokseen asti, jonka jälkeen korttimäärää kasvaa vain yhdellä kortilla joka kierros. Viiden pelaajan pelissä aloitetaan kahdella kortilla, ja korttimäärää kasvatetaan kahdella toisella kierroksella ja yhdellä muilla kierroksilla. c410ac883159764541bf0ef0114c55262e4de7c0 1199 1198 2021-06-20T19:39:33Z Sarkkukukka 5888 Parempi ulkonäkö wikitext text/x-wiki TAVOITE Sinun pitää arvata oikein voittamiesi tikkien määrä joka kierroksella saadaksesi mahdollisimman paljon pisteitä. Eniten pisteitä kerännyt voittaa! PELIN KULKU Sekoita kaikki 60 korttia, sisältäen 13 kääpiötä, 13 haltiaa, 13 jättiläistä, 13 ihmistä, 4 velhoa ja 4 narria. Jaa jokaiselle pelaajalle kierrosluvun verran kortteja. Seuraavaksi oikein päin käännetty kortti on valtti. Jos kortti on narri, kierroksella ei ole valttia. Jos kortti on velho, jakaja valitsee valtin. Viimeisellä kierroksella ei ole valttia. Jokainen pelaaja, aloittaen jakajan vasemmalla olevasta pelaajasta, arvaa kuinka monta tikkiä hän voittaa kierroksella. Jakajan vasemmalla puolella oleva pelaaja aloittaa tikin millä tahansa kortilla. Jos aloittava kortti on velho, se voittaa välttämättä. Jos kortti on narri, seuraava pelaaja päättää tikin värin. Jos tikissä pelataan vain narreja, ensimmäinen narri voittaa. Pelaajien täytyy seurata tikin väriä, jos mahdollista. Jos pelaaja ei siihen pysty, hän voi pelata toisen värin kortin, mukaan lukien valttiväriä. Velhoja ja narreja voi pelata milloin vain. Ensimmäinen pelattu velho voittaa tikin. Jos tikissä ei ole velhoja, sen voittaa korkein valttivärin kortti. Muussa tapauksessa sen voittaa korkein tikkivärin kortti. Tikin voittaja aloittaa seuraavan tikin. ARVAUS Jos pelaaja arvasi oikein tikkiensä määrän kierroksen alussa, he saavat 20 pistettä sekä 10 pistettä jokaista voitettua tikkiä kohden. Jos pelaaja arvasi väärin tikkiensä määrän, he menettävät 10 pistettä jokaista väärin arvattua tikkiä kohden. PELIN PÄÄTÖS Viimeinen kierros koittaa, kun kaikki kortit ovat jaettu. Kolmella pelaajalla pelataan näin 20 kierrosta, neljällä pelaajalla 15 kierrosta, viidellä 12 ja kuudella 6. Pelin lopussa eniten pisteitä kerännyt voittaa. VARIANTIT * Arvaaminen ** Piilotettu arvaus: arvaukset paljastetaan samaan aikaan ennen kuin kierros aloitetaan. ** Plus miinus 1: arvattujen tikkien määrä kierroksella ei saa olla sama kuin kierroksen tikkien määrä. ** Salainen arvaus: arvaukset paljastetaan vasta kierroksen päättyessä. * Pelin pituus ** 5 kierrosta: kaikki kortit jaetaan joka kierroksella. Peliä pelataan vain viisi kierrosta. ** Amigo-turnaussäännöt: Peliä pelataan 10 kierrosta tietyllä määrällä kortteja. Kolmen pelaajan pelissä, jokainen aloittaa kahdella kortilla ja korttimäärä kasvaa kahdella joka kierroksella. Neljän pelaajan pelissä aloitetaan yhdellä kortilla, ja korttimäärää kasvatetaan kahdella joka kierros kuudenteen kierrokseen asti, jonka jälkeen korttimäärää kasvaa vain yhdellä kortilla joka kierros. Viiden pelaajan pelissä aloitetaan kahdella kortilla, ja korttimäärää kasvatetaan kahdella toisella kierroksella ja yhdellä muilla kierroksilla. a6b9bd3dd4ddfb904bc3c0f56450d529f02be9eb Gamehelprage 0 191 1200 2021-08-24T14:55:49Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Päämäärä == Rage on tikinottopeli, jossa yritetään voittaa kierroksen alussa luvatun määrän tikkejä. Peli kestää 10 kierrosta, jonka jälkeen eniten pisteitä haalinut pelaaja voittaa pelin. Tasapeleissä eniten onnistuneita lupausta tehnyt voittaa. == Kortteja == Pelissä on 110 korttia: * 96 kpl: Pakassa on kuusi (6) maata (🟨 🟧 🟥 🟪 🟩 ja 🟦), jokaisessa kortteja numeroista 0-15 * 14 kpl: Pakassa on viisi (5) erilaista harmaata toimintakorttia ** 3 kpl (+5) - tikin ottanut pejaaja saa +5 pistettä ** 4 kpl Vaihda valttia (❗) - satunnaisesti vaihtaa valttiväriä ** 3 kpl (-5) - tikin ottanut pelaaja saa -5 ** 4 kpl Ei valttia (🚫) - ei valttia tällä tikillä, satunnainen valttikortti valitaan tikin jälkeen ** 2 kpl Jokeri (🃏) - tekee tästä korteista korkeanumeroisimman pelaajan päättämästä väristä (valttiväri mukaanlukien) == Säännöt == Pelissä on kymmenen (10) kierrosta. Kierroksessa on kolme vaihetta: === Jaa ja ennusta === Jaa 10 korttia pelaajille, yksi vähemmän jokaisesta pelatusta kierroksesta, kunnes viimeisellä kierroksella kaikille jaetaan vain yksi kortti. Nosta vielä yksi kortti valttimaaksi. Sitten pelaajat alkaen jakajasta vasemmalta tarjoavat vuorotellen sen määrän tikkejä, minkä arvioivat voittavansa kierroksella. === Pelaa tikkejä === Ensimmäinen tarjoaja aloittaa kierroksen pöytäämällä kortin tikkiin. Tikin voittaa korkea-arvoisimman kortin pelannut. Pelaajien on tunnustettava maata. Jos pelaaja ei pysty tunnustamaan, hän voi vapaasti pelata minkä tahansa kortin. Pelaaja voi aloittaa tikin myös toimintakortilla, jolloinka seuraava pöydätty väri on tikin maa. Tikin voittaa korkein pöydätty valttimaan kortti. Jos valttia ei pelattu, niin korkein aloittavan maan kortti. === Tarkistus ja pisteytys === Kun kierroksen kaikki kortit on pelattu, tarkistetaan tarjousten ja tikkien summa. Pisteet menevät seuraavasti: * +1 voitettu tikki * +10 tarjouksen verran tikkejä * -5 tikkejä yli/alle tarjotun määrän * +/-5 kortit voitetuissa tikeissä Tarjousvaiheen aloittanut on uusi jakaja. Uusi kierros alkaa ja pelaajille jaetaan yksi kortti vähemmän kortteja. 1ba029e52a07f95de04ae1dbdf7ca186f5e1bd49 Gamehelpwizard 0 190 1204 1199 2021-08-24T19:13:57Z Rexroom 5688 Siistin ulkoasua hieman wikitext text/x-wiki == Tavoite == Sinun pitää arvata oikein voittamiesi tikkien määrä joka kierroksella saadaksesi mahdollisimman paljon pisteitä. Eniten pisteitä kerännyt voittaa! == Pelin kulku == Sekoita kaikki 60 korttia, sisältäen 13 kääpiötä, 13 haltiaa, 13 jättiläistä, 13 ihmistä, 4 velhoa ja 4 narria. Jaa jokaiselle pelaajalle kierrosluvun verran kortteja. Seuraavaksi oikein päin käännetty kortti on valtti. Jos kortti on narri, kierroksella ei ole valttia. Jos kortti on velho, jakaja valitsee valtin. Viimeisellä kierroksella ei ole valttia. Jokainen pelaaja, aloittaen jakajan vasemmalla olevasta pelaajasta, arvaa kuinka monta tikkiä hän voittaa kierroksella. Jakajan vasemmalla puolella oleva pelaaja aloittaa tikin millä tahansa kortilla. Jos aloittava kortti on velho, se voittaa välttämättä. Jos kortti on narri, seuraava pelaaja päättää tikin värin. Jos tikissä pelataan vain narreja, ensimmäinen narri voittaa. Pelaajien täytyy seurata tikin väriä, jos mahdollista. Jos pelaaja ei siihen pysty, hän voi pelata toisen värin kortin, mukaan lukien valttiväriä. Velhoja ja narreja voi pelata milloin vain. Ensimmäinen pelattu velho voittaa tikin. Jos tikissä ei ole velhoja, sen voittaa korkein valttivärin kortti. Muussa tapauksessa sen voittaa korkein tikkivärin kortti. Tikin voittaja aloittaa seuraavan tikin. == Arvaus == Jos pelaaja arvasi oikein tikkiensä määrän kierroksen alussa, he saavat 20 pistettä sekä 10 pistettä jokaista voitettua tikkiä kohden. Jos pelaaja arvasi väärin tikkiensä määrän, he menettävät 10 pistettä jokaista väärin arvattua tikkiä kohden. == Pelin päätös == Viimeinen kierros koittaa, kun kaikki kortit ovat jaettu. Kolmella pelaajalla pelataan näin 20 kierrosta, neljällä pelaajalla 15 kierrosta, viidellä 12 ja kuudella 6. Pelin lopussa eniten pisteitä kerännyt voittaa. == Muunnelmat == === Arvaaminen === * Piilotettu arvaus: arvaukset paljastetaan samaan aikaan ennen kuin kierros aloitetaan. * Plus miinus 1: arvattujen tikkien määrä kierroksella ei saa olla sama kuin kierroksen tikkien määrä. * Salainen arvaus: arvaukset paljastetaan vasta kierroksen päättyessä. === Pelin pituus === * 5 kierrosta: kaikki kortit jaetaan joka kierroksella. Peliä pelataan vain viisi kierrosta. * Amigo-turnaussäännöt: Peliä pelataan 10 kierrosta tietyllä määrällä kortteja. Kolmen pelaajan pelissä, jokainen aloittaa kahdella kortilla ja korttimäärä kasvaa kahdella joka kierroksella. Neljän pelaajan pelissä aloitetaan yhdellä kortilla, ja korttimäärää kasvatetaan kahdella joka kierros kuudenteen kierrokseen asti, jonka jälkeen korttimäärää kasvaa vain yhdellä kortilla joka kierros. Viiden pelaajan pelissä aloitetaan kahdella kortilla, ja korttimäärää kasvatetaan kahdella toisella kierroksella ja yhdellä muilla kierroksilla. 523c347fbb96d57d548be165ea3d274d34acad27 Gamehelpwelcometo 0 192 1205 2021-09-06T12:47:27Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Päämäärä == Arkkitehtinä rakennat kolmeen eri katuun talonyhtiöitä taloja ryhmittämällä, yrittäen saada ensimmäisenä kolme asemakaavaa valmiiksi. == Pelin kulku == Pelaajat valitsevat ensin numeron ja toiminnon ja sitten kirjoittavat numeron yhteen kadun taloista. Sitten he ''voivat'' suorittaa toiminnon, jonka he valitsivat. '''Tärkeää!''' Talot kadulla seuraavat nousevaa numerojärjestystä. Voit kirjoittaa numeron minne tahansa, mutta et voi pienemmän numeron isomman jälkeen. Jos et syystä tai toisesta pysty kirjoittamaan numeroa, et voi käyttää toimintoa ja rakennuslupasi hylätään - saat merkinnän hylkäyssarakkeeseen. ''Jos joku pelaajista saa kolme hylkäystä, peli päättyy siihen kierrokseen.'' === Asemakaavat === Asemakaavat toimivat yhtenä pelin päämäärinä. Pelaajien on rakennettava asemakaava-korttien näyttämät taloyhtiöt. Kun saat tietyn kaavan määrittelemän määrän ja koon taloyhtiöitä valmiiksi, saat mahdollisuuden pisteyttää kyseisen asemakaavan. Asemakaavan ensimmäisenä samalla kierroksella valmiiksi saaneet saavat kortin pistemääristä isomman verran pisteitä. Kortti käännetään ympäri. Myöhemmät pelaajat saavat sitten pienemmän määrän pisteitä kun saavat kyseisen kaavan valmiiksi. Asemakaavaan käytettyjen taloyhtiöiden ylle piirretään viiva merkitsemään, ettei niitä voi käyttää uudestaan tai pilkkoa. Pelin ensimmäisen valmistuneen asemakaavan toteuttanut voi sekoittaa kaikki kortit uusiksi pinoiksi. ''Peli päättyy, kun joku pelaajista on saanut kolme asemakaavaa valmiiksi.'' == Toiminnot == === Maanmittaaja === Pelaaja voi rakentaa aidan kahden talon välille. Aidat rajaavat taloyhtiöitä, joista pelaaja saa pisteitä. Valmis taloyhtiö koostuu kahden aidan väliin rakennetuista taloista. Huomaa, että katujen kummassakin päissä on jo aita. ''Et voi asettaa aitaa taloyhtiöön, joka on merkitty jo käyttöön asemakaavassa.'' === Kiinteistönvälittäjä === Pelaaja voi nostaa valmistuneiden taloyhtiöiden arvoja täyttämällä ylimmän vapaan ruudun haluamastaan taloyhtiösarakkeesta. === Maisemoija === Pelaaja voi rakentaa puistoa täyttämällä vasemmanpuoleisen vapaan puistoruudun kyseiseltä kadulta, johon kirjoitti numeron. Puiston arvo on pienin näkyvä numero. === Uima-allasvalmistaja === Pelaaja voi rakentaa uima-altaan taloon, johon on suunniteltu uima-allas. Uima-altaan rakentaminen yliviivaa uima-allassarakkeen pienimmän numeron. === Vuokratyöläinen === Pelaaja voi muuttaa valitsemaansa numeroaan enintään kahdella, mahdollistaen 0- tai jopa 17-numeroisen talon. Nousevaa numerojärjestystä pitää silti noudattaa. Vuokratyöläisen palkkaaminen täyttää yhden ruudun vuokratyöläissarakkeesta; jos pelin lopussa sinulla on eniten vuokratyöläisiä merkitty, saat 7 pistettä, seuraavana 4 ja kolmantena 1. Tasatilanteessa kaikki tasoissa olevat saavat saman verran. === Bis === Pelaaja saa kirjata saman numeron uudelleen samalle kadulle. Talo pitää kirjoittaa samannumeroisen talon viereen ja sen pitää kuulua samaan taloyhtiöön koko pelin aikana. Bis-taloja voi olla useampia peräkkäin. Pisteytyslehtiöstä täytetään pienin numero bis-sarakkeesta. Menetät pienimmän näkyvän numeron verran pisteitä pelin lopussa. == Pelin loppu == Peli päättyy kolmella eri tavalla: * Joku pelaajista saa kolmannen hylätyn rakennusluvan * Joku pelaajista on täyttänyt kaikkien kolmen asemakaavan ehdot * Joku pelaajista on rakentanut kaikkien katujensa kaikki talot Käydään lopullinen pisteytys. Jos on tasapeli, ratkaistaan se 1 talon yhtiöiden, 2 talon yhtiöiden jne. määrällä. == Muunnelmat == === Yksinpeli === Voit pelata yksinpeliversiota luomalla manuaalisesti uuden pöydän, valitsemalla harjoitustilan ja asettamalla pelaajamääräksi yhden. Pelin kierroksen aiknana on nyt valittavissa vain kolme korttia, joista valitset yhden numerona ja toisen toimintona. Jos nostat solo-kortin, käännetään vielä kesken olevat asemakaava-kortit ympäri. Muuten peli toimii kuin normaalisti. === Vaativa pelimuoto === Peliin tulee mukaan vaativampia asemakaavoja. Pelaajat voivat vuoronsa aikana rakentaa yhden kahdesta liikenneympyrästään. Liikenneympyrä jakaa kadun kahtia, eli numerojärjestys alkaa taas liikenneympyrästä. Huomaa, että yksi liikenneympyrä maksaa 3 pistettä, kaksi 8 pistettä. 1c2147bfa8d881c1524aba15602c0ccd55ad8148 1206 1205 2021-09-06T12:48:31Z Rexroom 5688 wikitext text/x-wiki == Päämäärä == Arkkitehtinä rakennat kolmeen eri katuun taloyhtiöitä taloja ryhmittämällä, yrittäen saada ensimmäisenä kolme asemakaavaa valmiiksi. == Pelin kulku == Pelaajat valitsevat ensin numeron ja toiminnon ja sitten kirjoittavat numeron yhteen kadun taloista. Sitten he ''voivat'' suorittaa toiminnon, jonka he valitsivat. '''Tärkeää!''' Talot kadulla seuraavat nousevaa numerojärjestystä. Voit kirjoittaa numeron minne tahansa, mutta et voi pienemmän numeron isomman jälkeen. Jos et syystä tai toisesta pysty kirjoittamaan numeroa, et voi käyttää toimintoa ja rakennuslupasi hylätään - saat merkinnän hylkäyssarakkeeseen. ''Jos joku pelaajista saa kolme hylkäystä, peli päättyy siihen kierrokseen.'' === Asemakaavat === Asemakaavat toimivat yhtenä pelin päämäärinä. Pelaajien on rakennettava asemakaava-korttien näyttämät taloyhtiöt. Kun saat tietyn kaavan määrittelemän määrän ja koon taloyhtiöitä valmiiksi, saat mahdollisuuden pisteyttää kyseisen asemakaavan. Asemakaavan ensimmäisenä samalla kierroksella valmiiksi saaneet saavat kortin pistemääristä isomman verran pisteitä. Kortti käännetään ympäri. Myöhemmät pelaajat saavat sitten pienemmän määrän pisteitä kun saavat kyseisen kaavan valmiiksi. Asemakaavaan käytettyjen taloyhtiöiden ylle piirretään viiva merkitsemään, ettei niitä voi käyttää uudestaan tai pilkkoa. Pelin ensimmäisen valmistuneen asemakaavan toteuttanut voi sekoittaa kaikki kortit uusiksi pinoiksi. ''Peli päättyy, kun joku pelaajista on saanut kolme asemakaavaa valmiiksi.'' == Toiminnot == === Maanmittaaja === Pelaaja voi rakentaa aidan kahden talon välille. Aidat rajaavat taloyhtiöitä, joista pelaaja saa pisteitä. Valmis taloyhtiö koostuu kahden aidan väliin rakennetuista taloista. Huomaa, että katujen kummassakin päissä on jo aita. ''Et voi asettaa aitaa taloyhtiöön, joka on merkitty jo käyttöön asemakaavassa.'' === Kiinteistönvälittäjä === Pelaaja voi nostaa valmistuneiden taloyhtiöiden arvoja täyttämällä ylimmän vapaan ruudun haluamastaan taloyhtiösarakkeesta. === Maisemoija === Pelaaja voi rakentaa puistoa täyttämällä vasemmanpuoleisen vapaan puistoruudun kyseiseltä kadulta, johon kirjoitti numeron. Puiston arvo on pienin näkyvä numero. === Uima-allasvalmistaja === Pelaaja voi rakentaa uima-altaan taloon, johon on suunniteltu uima-allas. Uima-altaan rakentaminen yliviivaa uima-allassarakkeen pienimmän numeron. === Vuokratyöläinen === Pelaaja voi muuttaa valitsemaansa numeroaan enintään kahdella, mahdollistaen 0- tai jopa 17-numeroisen talon. Nousevaa numerojärjestystä pitää silti noudattaa. Vuokratyöläisen palkkaaminen täyttää yhden ruudun vuokratyöläissarakkeesta; jos pelin lopussa sinulla on eniten vuokratyöläisiä merkitty, saat 7 pistettä, seuraavana 4 ja kolmantena 1. Tasatilanteessa kaikki tasoissa olevat saavat saman verran. === Bis === Pelaaja saa kirjata saman numeron uudelleen samalle kadulle. Talo pitää kirjoittaa samannumeroisen talon viereen ja sen pitää kuulua samaan taloyhtiöön koko pelin aikana. Bis-taloja voi olla useampia peräkkäin. Pisteytyslehtiöstä täytetään pienin numero bis-sarakkeesta. Menetät pienimmän näkyvän numeron verran pisteitä pelin lopussa. == Pelin loppu == Peli päättyy kolmella eri tavalla: * Joku pelaajista saa kolmannen hylätyn rakennusluvan * Joku pelaajista on täyttänyt kaikkien kolmen asemakaavan ehdot * Joku pelaajista on rakentanut kaikkien katujensa kaikki talot Käydään lopullinen pisteytys. Jos on tasapeli, ratkaistaan se 1 talon yhtiöiden, 2 talon yhtiöiden jne. määrällä. == Muunnelmat == === Yksinpeli === Voit pelata yksinpeliversiota luomalla manuaalisesti uuden pöydän, valitsemalla harjoitustilan ja asettamalla pelaajamääräksi yhden. Pelin kierroksen aiknana on nyt valittavissa vain kolme korttia, joista valitset yhden numerona ja toisen toimintona. Jos nostat solo-kortin, käännetään vielä kesken olevat asemakaava-kortit ympäri. Muuten peli toimii kuin normaalisti. === Vaativa pelimuoto === Peliin tulee mukaan vaativampia asemakaavoja. Pelaajat voivat vuoronsa aikana rakentaa yhden kahdesta liikenneympyrästään. Liikenneympyrä jakaa kadun kahtia, eli numerojärjestys alkaa taas liikenneympyrästä. Huomaa, että yksi liikenneympyrä maksaa 3 pistettä, kaksi 8 pistettä. 294e5a3a6960a11d29ab9a70a0c9d455ee64e9f8 1231 1206 2022-03-13T12:10:47Z Rexroom 5688 /* Laajennukset */ wikitext text/x-wiki == Päämäärä == Arkkitehtinä rakennat kolmeen eri katuun taloyhtiöitä taloja ryhmittämällä, yrittäen saada ensimmäisenä kolme asemakaavaa valmiiksi. == Pelin kulku == Pelaajat valitsevat ensin numeron ja toiminnon ja sitten kirjoittavat numeron yhteen kadun taloista. Sitten he ''voivat'' suorittaa toiminnon, jonka he valitsivat. '''Tärkeää!''' Talot kadulla seuraavat nousevaa numerojärjestystä. Voit kirjoittaa numeron minne tahansa, mutta et voi pienemmän numeron isomman jälkeen. Jos et syystä tai toisesta pysty kirjoittamaan numeroa, et voi käyttää toimintoa ja rakennuslupasi hylätään - saat merkinnän hylkäyssarakkeeseen. ''Jos joku pelaajista saa kolme hylkäystä, peli päättyy siihen kierrokseen.'' === Asemakaavat === Asemakaavat toimivat yhtenä pelin päämäärinä. Pelaajien on rakennettava asemakaava-korttien näyttämät taloyhtiöt. Kun saat tietyn kaavan määrittelemän määrän ja koon taloyhtiöitä valmiiksi, saat mahdollisuuden pisteyttää kyseisen asemakaavan. Asemakaavan ensimmäisenä samalla kierroksella valmiiksi saaneet saavat kortin pistemääristä isomman verran pisteitä. Kortti käännetään ympäri. Myöhemmät pelaajat saavat sitten pienemmän määrän pisteitä kun saavat kyseisen kaavan valmiiksi. Asemakaavaan käytettyjen taloyhtiöiden ylle piirretään viiva merkitsemään, ettei niitä voi käyttää uudestaan tai pilkkoa. Pelin ensimmäisen valmistuneen asemakaavan toteuttanut voi sekoittaa kaikki kortit uusiksi pinoiksi. ''Peli päättyy, kun joku pelaajista on saanut kolme asemakaavaa valmiiksi.'' == Toiminnot == === Maanmittaaja === Pelaaja voi rakentaa aidan kahden talon välille. Aidat rajaavat taloyhtiöitä, joista pelaaja saa pisteitä. Valmis taloyhtiö koostuu kahden aidan väliin rakennetuista taloista. Huomaa, että katujen kummassakin päissä on jo aita. ''Et voi asettaa aitaa taloyhtiöön, joka on merkitty jo käyttöön asemakaavassa.'' === Kiinteistönvälittäjä === Pelaaja voi nostaa valmistuneiden taloyhtiöiden arvoja täyttämällä ylimmän vapaan ruudun haluamastaan taloyhtiösarakkeesta. === Maisemoija === Pelaaja voi rakentaa puistoa täyttämällä vasemmanpuoleisen vapaan puistoruudun kyseiseltä kadulta, johon kirjoitti numeron. Puiston arvo on pienin näkyvä numero. === Uima-allasvalmistaja === Pelaaja voi rakentaa uima-altaan taloon, johon on suunniteltu uima-allas. Uima-altaan rakentaminen yliviivaa uima-allassarakkeen pienimmän numeron. === Vuokratyöläinen === Pelaaja voi muuttaa valitsemaansa numeroaan enintään kahdella, mahdollistaen 0- tai jopa 17-numeroisen talon. Nousevaa numerojärjestystä pitää silti noudattaa. Vuokratyöläisen palkkaaminen täyttää yhden ruudun vuokratyöläissarakkeesta; jos pelin lopussa sinulla on eniten vuokratyöläisiä merkitty, saat 7 pistettä, seuraavana 4 ja kolmantena 1. Tasatilanteessa kaikki tasoissa olevat saavat saman verran. === Bis === Pelaaja saa kirjata saman numeron uudelleen samalle kadulle. Talo pitää kirjoittaa samannumeroisen talon viereen ja sen pitää kuulua samaan taloyhtiöön koko pelin aikana. Bis-taloja voi olla useampia peräkkäin. Pisteytyslehtiöstä täytetään pienin numero bis-sarakkeesta. Menetät pienimmän näkyvän numeron verran pisteitä pelin lopussa. == Pelin loppu == Peli päättyy kolmella eri tavalla: * Joku pelaajista saa kolmannen hylätyn rakennusluvan * Joku pelaajista on täyttänyt kaikkien kolmen asemakaavan ehdot * Joku pelaajista on rakentanut kaikkien katujensa kaikki talot Käydään lopullinen pisteytys. Jos on tasapeli, ratkaistaan se 1 talon yhtiöiden, 2 talon yhtiöiden jne. määrällä. == Muunnelmat == === Yksinpeli === Voit pelata yksinpeliversiota luomalla manuaalisesti uuden pöydän, valitsemalla harjoitustilan ja asettamalla pelaajamääräksi yhden. Pelin kierroksen aikana on nyt valittavissa vain kolme korttia, joista valitset yhden numerona ja toisen toimintona. Jos nostat solo-kortin, käännetään vielä kesken olevat asemakaava-kortit ympäri. Muuten peli toimii kuin normaalisti. === Vaativa pelimuoto === Peliin tulee mukaan vaativampia asemakaavoja. Pelaajat voivat vuoronsa aikana rakentaa yhden kahdesta liikenneympyrästään. Liikenneympyrä jakaa kadun kahtia, eli numerojärjestys alkaa taas liikenneympyrästä. Huomaa, että yksi liikenneympyrä maksaa 3 pistettä, kaksi 8 pistettä. == Laajennukset == === Jäätelöauto === Kaduilla on nyt merkitty jäätelötötteröin tontteja, johon voi myydä jäätelöä. Kun täytät yhden kadun tonteista, pyyhi yli '''kaikki''' ympyröimättömät tötteröt jäätelöauton tulosuunnasta täytettävään tonttiin. Jos kyseisessä tontissa on tötterö, ympyröi se. Muut pelaajat pyyhkivät saman tontin tötterön omista lehtiöistään. Kolmannella kadulla voit valita jäätelöauton tulosuunnan. Pelin lopussa ympyröidyt jäätelötötteröt ovat yhden pisteen arvoisia. === Jouluvalot === Koristele tontit jouluvaloilla kirjoittamalla peräkkäisiä numeroita tontteihin. (esim. 5-6-7) Bis-numerot luetaan peräkkäisiksi. Pelin lopussa kolmen kadun pisimmät jouluvaloketjut pisteytetään. 8fb2efa4304f8836548a5e13b26a251ce7b9603f 1232 1231 2022-03-13T12:12:34Z Rexroom 5688 /* Yksinpeli */ wikitext text/x-wiki == Päämäärä == Arkkitehtinä rakennat kolmeen eri katuun taloyhtiöitä taloja ryhmittämällä, yrittäen saada ensimmäisenä kolme asemakaavaa valmiiksi. == Pelin kulku == Pelaajat valitsevat ensin numeron ja toiminnon ja sitten kirjoittavat numeron yhteen kadun taloista. Sitten he ''voivat'' suorittaa toiminnon, jonka he valitsivat. '''Tärkeää!''' Talot kadulla seuraavat nousevaa numerojärjestystä. Voit kirjoittaa numeron minne tahansa, mutta et voi pienemmän numeron isomman jälkeen. Jos et syystä tai toisesta pysty kirjoittamaan numeroa, et voi käyttää toimintoa ja rakennuslupasi hylätään - saat merkinnän hylkäyssarakkeeseen. ''Jos joku pelaajista saa kolme hylkäystä, peli päättyy siihen kierrokseen.'' === Asemakaavat === Asemakaavat toimivat yhtenä pelin päämäärinä. Pelaajien on rakennettava asemakaava-korttien näyttämät taloyhtiöt. Kun saat tietyn kaavan määrittelemän määrän ja koon taloyhtiöitä valmiiksi, saat mahdollisuuden pisteyttää kyseisen asemakaavan. Asemakaavan ensimmäisenä samalla kierroksella valmiiksi saaneet saavat kortin pistemääristä isomman verran pisteitä. Kortti käännetään ympäri. Myöhemmät pelaajat saavat sitten pienemmän määrän pisteitä kun saavat kyseisen kaavan valmiiksi. Asemakaavaan käytettyjen taloyhtiöiden ylle piirretään viiva merkitsemään, ettei niitä voi käyttää uudestaan tai pilkkoa. Pelin ensimmäisen valmistuneen asemakaavan toteuttanut voi sekoittaa kaikki kortit uusiksi pinoiksi. ''Peli päättyy, kun joku pelaajista on saanut kolme asemakaavaa valmiiksi.'' == Toiminnot == === Maanmittaaja === Pelaaja voi rakentaa aidan kahden talon välille. Aidat rajaavat taloyhtiöitä, joista pelaaja saa pisteitä. Valmis taloyhtiö koostuu kahden aidan väliin rakennetuista taloista. Huomaa, että katujen kummassakin päissä on jo aita. ''Et voi asettaa aitaa taloyhtiöön, joka on merkitty jo käyttöön asemakaavassa.'' === Kiinteistönvälittäjä === Pelaaja voi nostaa valmistuneiden taloyhtiöiden arvoja täyttämällä ylimmän vapaan ruudun haluamastaan taloyhtiösarakkeesta. === Maisemoija === Pelaaja voi rakentaa puistoa täyttämällä vasemmanpuoleisen vapaan puistoruudun kyseiseltä kadulta, johon kirjoitti numeron. Puiston arvo on pienin näkyvä numero. === Uima-allasvalmistaja === Pelaaja voi rakentaa uima-altaan taloon, johon on suunniteltu uima-allas. Uima-altaan rakentaminen yliviivaa uima-allassarakkeen pienimmän numeron. === Vuokratyöläinen === Pelaaja voi muuttaa valitsemaansa numeroaan enintään kahdella, mahdollistaen 0- tai jopa 17-numeroisen talon. Nousevaa numerojärjestystä pitää silti noudattaa. Vuokratyöläisen palkkaaminen täyttää yhden ruudun vuokratyöläissarakkeesta; jos pelin lopussa sinulla on eniten vuokratyöläisiä merkitty, saat 7 pistettä, seuraavana 4 ja kolmantena 1. Tasatilanteessa kaikki tasoissa olevat saavat saman verran. === Bis === Pelaaja saa kirjata saman numeron uudelleen samalle kadulle. Talo pitää kirjoittaa samannumeroisen talon viereen ja sen pitää kuulua samaan taloyhtiöön koko pelin aikana. Bis-taloja voi olla useampia peräkkäin. Pisteytyslehtiöstä täytetään pienin numero bis-sarakkeesta. Menetät pienimmän näkyvän numeron verran pisteitä pelin lopussa. == Pelin loppu == Peli päättyy kolmella eri tavalla: * Joku pelaajista saa kolmannen hylätyn rakennusluvan * Joku pelaajista on täyttänyt kaikkien kolmen asemakaavan ehdot * Joku pelaajista on rakentanut kaikkien katujensa kaikki talot Käydään lopullinen pisteytys. Jos on tasapeli, ratkaistaan se 1 talon yhtiöiden, 2 talon yhtiöiden jne. määrällä. == Muunnelmat == === Yksinpeli === Voit pelata yksinpeliversiota luomalla manuaalisesti uuden pöydän, valitsemalla harjoitustilan ja asettamalla pelaajamääräksi yhden. Pelin kierroksen aikana on nyt valittavissa vain kolme korttia, joista valitset yhden numerona ja toisen toimintona. Jos nostat solo-kortin, käännetään vielä kesken olevat asemakaava-kortit ympäri. Muuten peli toimii kuin normaalisti. Kolmen päättymisehdon lisäksi peli päättyy nostopakan ehtymiseen. === Vaativa pelimuoto === Peliin tulee mukaan vaativampia asemakaavoja. Pelaajat voivat vuoronsa aikana rakentaa yhden kahdesta liikenneympyrästään. Liikenneympyrä jakaa kadun kahtia, eli numerojärjestys alkaa taas liikenneympyrästä. Huomaa, että yksi liikenneympyrä maksaa 3 pistettä, kaksi 8 pistettä. == Laajennukset == === Jäätelöauto === Kaduilla on nyt merkitty jäätelötötteröin tontteja, johon voi myydä jäätelöä. Kun täytät yhden kadun tonteista, pyyhi yli '''kaikki''' ympyröimättömät tötteröt jäätelöauton tulosuunnasta täytettävään tonttiin. Jos kyseisessä tontissa on tötterö, ympyröi se. Muut pelaajat pyyhkivät saman tontin tötterön omista lehtiöistään. Kolmannella kadulla voit valita jäätelöauton tulosuunnan. Pelin lopussa ympyröidyt jäätelötötteröt ovat yhden pisteen arvoisia. === Jouluvalot === Koristele tontit jouluvaloilla kirjoittamalla peräkkäisiä numeroita tontteihin. (esim. 5-6-7) Bis-numerot luetaan peräkkäisiksi. Pelin lopussa kolmen kadun pisimmät jouluvaloketjut pisteytetään. d45170792132d49371c855f95bd461128d81dd4f Gamehelpescapefromthehiddencastle 0 193 1207 2021-09-08T18:05:34Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Päämäärä == Pelaaja johdattaa vieraitaan Hugo-kummitukselta turvaan, joka yrittää pelotella heitä nappaamalla heitä kellariinsa. == Pelin kulku == === Alkuasettelu === Pelin alussa pelaajat asettelevat vieraitaan portaikkoa ympäröivään galleriakäytävälle. Tässä vaiheessa samalle ruudulle ei voi asettaa useampia vieraita. === Juhlat alkavat === Pelaajat heittävät vuorotellen noppaa. Pelaajat voivat siirrellä silmäluvun verran yhtä vierastaa. Jos pelaaja heittää HUGOn, kummitusta siirretään oikean yläkulman huoneen osoittaman määrän ruutuja. Aina kun Hugo kiertää gallerian kokonaan, Hugo Jos Hugo saapuu galleriaan, pelaajat voivat siirtää vieraitaan huoneisiin. Huoneet luetaan yhdeksi ruuduksi. Jos huoneessa on jo joku vieras, huoneeseen vaaditaan tasanumero, jolloin hänet häädetään huoneesta. Jos huone on merkitty vihreällä kilvellä, hän vähentää kilven osoittaman luvun verran säikkypisteitä ja lisää jos se on punainen. Hugon saa vieraan kiinni, jos hän joko saapuu tai ohittaa vieraan tai vieraat pitämän ruudun. Silloin vieraat viedään kellarin portaiden alimpaan vapaaseen portaikkoon järjestyksessä, alkaen ensimmäisenä kiinnijäänneet. Jos ruudussa oli useampia vieraita, heidät kohdellaan yhtenä ryhmänä ja asetetaan samaan portaikkoon. Pelaajat voivat omalla vuorollaan siirtää vieraansa pois portaikosta kuin normaalisti. Pelaajat saavat askeleessa merkityn verran säikkypisteitä. '''Huom!''' Jos portaikko on täynnä vieraista, portaikko täyttyy taas alimmasta askeleesta. == Pelin lopppu == Peli päättyy kahdella tapaa: * Joku pelaajista kerää 46 pelkopistettä tai yli * HUGO on kiertänyt galleriaa 6 kertaa Pelin voittaja on se, jolla on vähiten pelkopisteitä. dc9ea5a84fd1e005f449398583e17df3620ac8d9 1208 1207 2021-09-08T18:07:45Z Rexroom 5688 /* Juhlat alkavat */ wikitext text/x-wiki == Päämäärä == Pelaaja johdattaa vieraitaan Hugo-kummitukselta turvaan, joka yrittää pelotella heitä nappaamalla heitä kellariinsa. == Pelin kulku == === Alkuasettelu === Pelin alussa pelaajat asettelevat vieraitaan portaikkoa ympäröivään galleriakäytävälle. Tässä vaiheessa samalle ruudulle ei voi asettaa useampia vieraita. === Juhlat alkavat === Pelaajat heittävät vuorotellen noppaa. Pelaajat voivat siirrellä silmäluvun verran yhtä vierastaa. Jos pelaaja heittää HUGOn, kummitusta siirretään oikean yläkulman huoneen osoittaman määrän ruutuja. Aina kun Hugo kiertää gallerian kokonaan, Hugon vauhti nousee 1 ruudulla. Jos Hugo saapuu galleriaan, pelaajat voivat siirtää vieraitaan huoneisiin. Huoneet luetaan yhdeksi ruuduksi. Jos huoneessa on jo joku vieras, huoneeseen vaaditaan tasanumero, jolloin hänet häädetään huoneesta. Jos huone on merkitty vihreällä kilvellä, hän vähentää kilven osoittaman luvun verran säikkypisteitä ja lisää jos se on punainen. Hugon saa vieraan kiinni, jos hän joko saapuu tai ohittaa vieraan tai vieraat pitämän ruudun. Silloin vieraat viedään kellarin portaiden alimpaan vapaaseen portaikkoon järjestyksessä, alkaen ensimmäisenä kiinnijäänneet. Jos ruudussa oli useampia vieraita, heidät kohdellaan yhtenä ryhmänä ja asetetaan samaan portaikkoon. Pelaajat voivat omalla vuorollaan siirtää vieraansa pois portaikosta kuin normaalisti. Pelaajat saavat askeleessa merkityn verran säikkypisteitä. '''Huom!''' Jos portaikko on täynnä vieraista, portaikko täyttyy taas alimmasta askeleesta. == Pelin lopppu == Peli päättyy kahdella tapaa: * Joku pelaajista kerää 46 pelkopistettä tai yli * HUGO on kiertänyt galleriaa 6 kertaa Pelin voittaja on se, jolla on vähiten pelkopisteitä. daa77f4a416e77a64bbc3e8ad8643e2c337a9dac 1212 1208 2021-09-10T12:13:54Z Rexroom 5688 Tekstihuoltoa ja vieraiden määrät wikitext text/x-wiki == Päämäärä == Pelaaja johdattaa vieraitaan Hugo-kummitukselta turvaan, joka yrittää napata heidät kauhujen kellariinsa. == Vieraiden (pelaajamerkkien) määrä == * 2 pelaajaa: 6 vierasta per pelaaja * 3 pelaajaa: 5 vierasta * 4 pelaajaa: 4 vierasta * 5 pelaajaa: 3 vierasta * 6–8 pelaajaa: 2 vierasta == Pelin kulku == === Alkuasettelu === Pelin alussa pelaajat asettelevat vieraitaan portaikkoa ympäröivään galleriakäytävälle. Tässä vaiheessa samalle ruudulle ei voi asettaa useampia vieraita. === Juhlat alkavat === Pelaajat heittävät vuorotellen noppaa. Pelaajat voivat siirrellä silmäluvun verran yhtä vierastaa. Jos pelaaja heittää HUGOn, kummitusta siirretään oikean yläkulman huoneen osoittaman määrän ruutuja. Aina kun Hugo kiertää gallerian kokonaan, Hugon vauhti nousee 1 ruudulla. ''Kun Hugo saapuu galleriaan, pelaajat voivat siirtää vieraitaan huoneisiin.''' Huoneet luetaan yhdeksi ruuduksi. Jos huoneessa on jo joku vieras, huoneeseen vaaditaan tasanumero, jolloin hänet häädetään huoneesta. Jos huone on merkitty vihreällä kilvellä, hän vähentää kilven osoittaman luvun verran säikkypisteitä ja lisää jos se on punainen. Hugon saa vieraan kiinni, jos hän joko saapuu tai ohittaa vieraan tai vieraat pitämän ruudun. Silloin vieraat viedään kellarin portaiden alimpaan vapaaseen portaikkoon järjestyksessä, alkaen ensimmäisenä kiinnijäänneet. Jos ruudussa oli useampia vieraita, heidät kohdellaan yhtenä ryhmänä ja asetetaan samaan portaikkoon. Pelaajat voivat omalla vuorollaan siirtää vieraansa pois portaikosta kuin normaalisti. Pelaajat saavat askeleessa merkityn verran säikkypisteitä. '''Huom!''' Jos portaikko on täynnä vieraista, portaikko täyttyy taas alimmasta askeleesta. == Pelin lopppu == Peli päättyy kahdella tapaa: * Joku pelaajista kerää 46 pelkopistettä tai yli * HUGO on kiertänyt gallerian 6 kertaa Vähiten pelkopisteitä kerännyt voittaa. f9c91acb6909f33150b2f02f8092c35047d98613 1223 1212 2022-02-01T12:24:35Z Rexroom 5688 /* Pelin lopppu */ typo wikitext text/x-wiki == Päämäärä == Pelaaja johdattaa vieraitaan Hugo-kummitukselta turvaan, joka yrittää napata heidät kauhujen kellariinsa. == Vieraiden (pelaajamerkkien) määrä == * 2 pelaajaa: 6 vierasta per pelaaja * 3 pelaajaa: 5 vierasta * 4 pelaajaa: 4 vierasta * 5 pelaajaa: 3 vierasta * 6–8 pelaajaa: 2 vierasta == Pelin kulku == === Alkuasettelu === Pelin alussa pelaajat asettelevat vieraitaan portaikkoa ympäröivään galleriakäytävälle. Tässä vaiheessa samalle ruudulle ei voi asettaa useampia vieraita. === Juhlat alkavat === Pelaajat heittävät vuorotellen noppaa. Pelaajat voivat siirrellä silmäluvun verran yhtä vierastaa. Jos pelaaja heittää HUGOn, kummitusta siirretään oikean yläkulman huoneen osoittaman määrän ruutuja. Aina kun Hugo kiertää gallerian kokonaan, Hugon vauhti nousee 1 ruudulla. ''Kun Hugo saapuu galleriaan, pelaajat voivat siirtää vieraitaan huoneisiin.''' Huoneet luetaan yhdeksi ruuduksi. Jos huoneessa on jo joku vieras, huoneeseen vaaditaan tasanumero, jolloin hänet häädetään huoneesta. Jos huone on merkitty vihreällä kilvellä, hän vähentää kilven osoittaman luvun verran säikkypisteitä ja lisää jos se on punainen. Hugon saa vieraan kiinni, jos hän joko saapuu tai ohittaa vieraan tai vieraat pitämän ruudun. Silloin vieraat viedään kellarin portaiden alimpaan vapaaseen portaikkoon järjestyksessä, alkaen ensimmäisenä kiinnijäänneet. Jos ruudussa oli useampia vieraita, heidät kohdellaan yhtenä ryhmänä ja asetetaan samaan portaikkoon. Pelaajat voivat omalla vuorollaan siirtää vieraansa pois portaikosta kuin normaalisti. Pelaajat saavat askeleessa merkityn verran säikkypisteitä. '''Huom!''' Jos portaikko on täynnä vieraista, portaikko täyttyy taas alimmasta askeleesta. == Pelin loppu == Peli päättyy kahdella tapaa: * Joku pelaajista kerää 46 pelkopistettä tai yli * HUGO on kiertänyt gallerian 6 kertaa Vähiten pelkopisteitä kerännyt voittaa. 85efee1d8521dc0b806f5f23546bfdd814fec528 Ohje 0 167 1209 1203 2021-09-08T18:10:48Z Rexroom 5688 /* Pelattavia */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpescapefromthehiddencastle|Keskiyön kummitus]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelplama|L.A.M.A.]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelproomtwentyfive|Room Twentyfive]] 2fa85e114faf480e878cfbaa9f8d73bb24a745ba 1211 1209 2021-09-09T19:37:14Z Rexroom 5688 /* Pelattavia */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabandonallartichokes|Abandon All Artichokes]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpescapefromthehiddencastle|Keskiyön kummitus]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelplama|L.A.M.A.]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelproomtwentyfive|Room Twentyfive]] 64d933aa007d15059938f1197c5036e90d041bfa 1213 1211 2021-09-13T12:57:33Z Rexroom 5688 /* Lokalisointi */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] Lokalisoinnit * [[Muistiinpanoja]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabandonallartichokes|Abandon All Artichokes]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpescapefromthehiddencastle|Keskiyön kummitus]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelplama|L.A.M.A.]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelproomtwentyfive|Room Twentyfive]] aa5c3767f5dbf7ddb03fd2c6a6d9415f1bc0b0cb 1234 1213 2022-07-20T10:06:36Z Rexroom 5688 /* Beeta-pelit */ wikitext text/x-wiki Tervetuloa suomenkieliseen BoardGameArena-sivuston wikiin. == Lokalisointi == Vinkkejä ja työkaluja pelien ja sivuston laadukkaaseen suomentamiseen. * [[Sanakirja]] * [[Suomentaminen]] Suunnitelmia ja ongelmakohtia. * [[Ongelmakohdat]] Lokalisoinnit * [[Muistiinpanoja]] == Peliohjeet == === Pelattavia === {| ! #-H ! I-R ! S-Ö |- style="vertical-align:top;" | * [[Gamehelpthirteenclues|13 Clues]] * [[Gamehelpsechsnimmt|6 Nimmt!]] * [[Gamehelpsevenwonders|7 Wonders]] * [[Gamehelpsevenwondersduel|7 Wonders Duel]] * [[Gamehelpeightmastersrevenge|8 Master's Revenge]] * [[Gamehelpninetynine|99 (tikkipeli)]] * [[Gamehelpabalone|Abalone]] * [[Gamehelpabandonallartichokes|Abandon All Artichokes]] * [[Gamehelpabyss|Abyss]] * [[Gamehelpfistfulofgold|A Fistful of Gold]] * [[Gamehelpalhambra|Alhambra]] * [[Gamehelpalveole|Alveole]] * [[Gamehelpamyitis|Amyitis]] * [[Gamehelpapocalypse|Apocalypse at the Zoo of Carson City]] * [[Gamehelparmadora|Armadöra]] * [[Gamehelpassyria|Assyria]] * [[Gamehelpautomobiles|Automobiles]] * [[Gamehelpbackgammon|Backgammon]] * [[Gamehelpballoonpop|Balloon Pop!]] * [[Gamehelpbandido|Bandido]] * [[Gamehelpbarbu|Barbu]] * [[Gamehelpbattleforhill|The Battle for Hill 218]] * [[Gamehelpbattleoflits|Battle of LITS]] * [[Gamehelpbattlesheep|Battle Sheep]] * [[Gamehelpbelote|Belote]] * [[Gamehelpbeyondthesun|Beyond the Sun]] * [[Gamehelpbids|Bids]] * [[Gamehelpbigtimesoccer|Big Time Soccer]] * [[Gamehelpbiyi|Biyi]] * [[Gamehelpblaze|Blaze]] * [[Gamehelpblooms|Blooms]] * [[Gamehelpblueskies|Blue Skies]] * [[Gamehelpbobail|Bobail]] * [[Gamehelpbombay|Bombay]] * [[Gamehelpboomerangaustralia|Boomerang: Australia]] * [[Gamehelptheboss|The Boss]] * [[Gamehelpbriscola|Briscola]] * [[Gamehelpbubbleepop|Bubblee Pop]] * [[gamehelpbug|Bug]] * [[Gamehelpthebuildersantiquity|The Builders: Antiquity]] * [[Gamehelpthebuilders|The Builders: Middle Ages]] * [[Gamehelpbutterfly|Butterfly]] * [[Gamehelpbuttons|Buttons]] * [[Gamehelpcacao|Cacao]] * [[Gamehelpcantstop|Can't Stop]] * [[Gamehelpcaravan|Caravan]] * [[Gamehelpcarcassonne|Carcassonne]] * [[Gamehelpcarcassonnehuntersandgatherers|Carcassonne: Kivikausi]] * [[Gamehelpcardiceo|Cardiceo]] * [[Gamehelpcaribbeanallfours|Caribbean All Fours]] * [[Gamehelpcarnegie|Carnegie]] * [[Gamehelpcaylus|Caylus]] * [[Gamehelpcelestia|Celestia]] * [[Gamehelpchakra|Chakra]] * [[Gamehelpchinagold|China Gold]] * [[Gamehelpcinco|Cinco]] * [[Gamehelpcircleoflife|Circle of Life]] * [[Gamehelpcityofthebigshoulders|City of the Big Shoulders]] * [[Gamehelpclansofcaledonia|Clans of Caledonia]] * [[Gamehelpcodexnaturalis|CODEX Naturalis]] * [[Gamehelpcoinche|Coinche]] * [[Gamehelpcolorpop|Color Pop]] * [[Gamehelpcoloretto|Coloretto]] * [[Gamehelpcoltexpress|Colt Express]] * [[Gamehelpconcept|Concept]] * [[Gamehelpconnectsix|Connect6]] * [[Gamehelpconsonar|Con Sonar!]] * [[Gamehelpconspiracy|Conspiracy]] * [[Gamehelpcoup|Coup]] * [[Gamehelpcrazyfarmers|Crazy Farmers]] * [[Gamehelpthecrew|The Crew]] * [[Gamehelpcribbage|Cribbage]] * [[Gamehelpcubirds|CuBirds]] * [[Gamehelpdarkagent|Dark Agent]] * [[Gamehelpdetectivepoker|Detective Poker]] * [[Gamehelpdiams|Diam's]] * [[Gamehelpdiceforge|Dice Forge]] * [[Gamehelpdicehospital|Dice Hospital]] * [[Gamehelpdicesummoners|Dice Summoners]] * [[Gamehelpdingosdreams|Dingo's Dreams]] * [[Gamehelpdinosaurteaparty|Dinosaur Tea Party]] * [[Gamehelpdjambi|Djambi]] * [[Gamehelpdodo|Dodo]] * [[Gamehelpdownforce|Downforce]] * [[Gamehelpdraftosaurus|Draftosaurus]] * [[Gamehelpdragoncastle|Dragon Castle]] * [[Gamehelpdragonheart|Dragonheart]] * [[Gamehelpdragonline|Dragon Line]] * [[Gamehelpdragonkeeper|Dragon Keeper: The Dungeon]] * [[Gamehelpdragonwood|Dragonwood]] * [[Gamehelpdungeonpetz|Dungeon Petz]] * [[Gamehelpdungeonroll|Dungeon Roll]] * [[Gamehelpdungeontwister|Dungeon Twister]] * [[Gamehelpelfenland|Elfenland]] * [[Gamehelpeminentdomain|Eminent Domain]] * [[Gamehelpemdomicrocosm|Eminent Domain: Microcosm]] * [[Gamehelpexplorationwarzone|Exploration: Warzone]] * [[Gamehelpakeruption|Eruption]] * [[Gamehelpevo|Evo: The "Game no Name"]] * [[Gamehelpnorthwestpassage|Expedition: Northwest Passage]] * [[Gamehelpfinity|Finity]] * [[Gamehelpflamingpyramids|Flaming Pyramids]] * [[Gamehelpfleet|Fleet]] * [[Gamehelpflorenzacardgame|Florenza: The Card Game]] * [[Gamehelpfluxx|Fluxx]] * [[Gamehelpforsale|For Sale]] * [[Gamehelpfourcolorcards|Four Color Cards]] * [[Gamehelpgaia|Gaia]] * [[Gamehelpgearnpiston|Gear & Piston]] * [[Gamehelpgo|Go]] * [[Gamehelpgoldwest|Gold West]] * [[Gamehelpgomoku|Gomoku]] * [[Gamehelpgonutsfordonuts|Go Nuts for Donuts]] * [[Gamehelpgopher|Gopher]] * [[Gamehelpgorami|GORami]] * [[Gamehelpgosu|Gosu]] * [[Gamehelpgrandbazaar|Grand Bazaar]] * [[Gamehelpgrosstarock|Grosstarock]] * [[Gamehelpguildes|Guildes]] * [[Gamehelpgyges|Gygès]] * [[Gamehelphacktrick|Hack Trick]] * [[Gamehelphaggis|Haggis]] * [[Gamehelphaiclue|Haiclue]] * [[Gamehelphappycity|Happy City]] * [[Gamehelphardback|Hardback]] * [[Gamehelphawaii|Hawaii]] * [[Gamehelphanabi|Hanabi]] * [[Gamehelphandandfoot|Hand and Foot]] * [[Gamehelpherrlof|Herrlof]] * [[Gamehelpherooj|Herooj]] * [[Gamehelphearts|Herttamisääri]] * [[Gamehelphex|Hex]] * [[Gamehelphive|Hive]] * [[Gamehelphoarders|Hoarders]] * [[Gamehelphomesteaders|Homesteaders]] * [[Gamehelphypnosia|Hypnosia]] | * [[Gamehelpicecoldicehockey|Ice Cold Ice Hockey]] * [[Gamehelpillustori|Illustori]] * [[Gamehelpincangold|Incan Gold]] * [[Gamehelpintheyearofthedragon|In the Year of the Dragon]] * [[Gamehelpinjawara|Injawara]] * [[Gamehelpinnovation|Innovation]] * [[Gamehelpissac|Issac]] * [[Gamehelpiwari|Iwari]] * [[Gamehelpjaipur|Jaipur]] * [[Gamehelpjekyllvshide|Jekyll vs. Hyde]] * [[Gamehelpthejellymonsterlab|The Jelly Monster Lab]] * [[Gamehelpjumpgate|Jump Gate]] * [[Gamehelpjustdesserts|Just Desserts]] * [[Gamehelpk2|K2]] * [[Gamehelpkabaleo|Kabaleo]] * [[Gamehelpkahuna|Kahuna]] * [[Gamehelpkalah|Kalah]] * [[Gamehelpkami|Kami]] * [[Gamehelpescapefromthehiddencastle|Keskiyön kummitus]] * [[Gamehelpkeyflower|Keyflower]] * [[Gamehelpkhronos|Khronos]] * [[Gamehelpchinesecheckers|Kiinanshakki]] * [[Gamehelpkingdombuilder|Kingdom Builder]] * [[Gamehelpkingdomino|Kingdomino]] * [[Gamehelpthekingsguild|The King's Guild]] * [[Gamehelpklaverjassen|Klaverjassen]] * [[Gamehelpkoikoi|Koi-koi]] * [[Gamehelpvultureculture|Korppikotka]] * [[Gamehelpkrosmasterarena|Krosmaster Arena]] * [[Gamehelpkrosmasterblast|Krosmaster Blast]] * [[Gamehelpkoryo|Koryŏ]] * [[Gamehelpkqj|KQJ]] * [[Gamehelplagranja|La Granja]] * [[Gamehelplama|L.A.M.A.]] * [[Gamehelpbattleship|Laivanupotus]] * [[Gamehelpledernierpeuple|Le Dernier Peuple]] * [[Gamehelplegendaryinventors|Legendary Inventors]] * [[Gamehelplettertycoon|Letter Tycoon]] * [[Gamehelplewisandclark|Lewis and Clark]] * [[Gamehelplibertalia|Libertalia]] * [[Gamehelplinesofaction|Lines of Action]] * [[Gamehelplogger|Logger]] * [[Gamehelplordsofxidit|Lords of Xidit]] * [[Gamehelplostcities|Lost Cities]] * [[Gamehelparnak|Lost Ruins of Arnak]] * [[Gamehelploveletter|Love Letter]] * [[Gamehelpluckynumbers|Lucky Numbers]] * [[Gamehelpohhell|Lupaus]] * [[Gamehelpluxor|Luxor]] * [[Gamehelpmachiavelli|Machiavelli]] * [[Gamehelpmadeira|Madeira]] * [[Gamehelpmammalath|Mammalath]] * [[Gamehelpmapmaker|Mapmaker: The Gerrymandering Game]] * [[Gamehelpmarcopolotwo|Marco Polo II: In the Service of the Khan]] * [[Gamehelpmarrakech|Marrakech]] * [[Gamehelpmarram|Marram]] * [[Gamehelpmartiandice|Martian Dice]] * [[Gamehelpmattock|Mattock]] * [[Gamehelpmeadow|Meadow]] * [[Gamehelpmedina|Medina]] * [[Gamehelpmetromania|Metromania]] * [[Gamehelpmijnlieff|Mijnlieff]] * [[Gamehelpmonsterfactory|Monster Factory]] * [[Gamehelpmrjack|Mr. Jack]] * [[Gamehelpmurusgallicus|Murus Gallicus]] * [[Gamehelpninemensmorris|Mylly]] * [[Gamehelpnangaparbat|Nanga Parbat]] * [[Gamehelpnautilus|Nautilus]] * [[Gamehelpconnectfour|Neljän suora]] * [[Gamehelpneutreeko|Neutreeko]] * [[Gamehelpniagara|Niagara]] * [[Gamehelpnidavellir|Nidavellir]] * [[Gamehelpnile|Nile]] * [[Gamehelpninetynine|Ninety-Nine]] * [[Gamehelpnippon|Nippon]] * [[Gamehelpnoirkvi|Noir: Killer versus Inspector]] * [[Gamehelpnotalone|Not Alone]] * [[Gamehelpnothanks|No Thanks!]] * [[Gamehelpnxs|NXS]] * [[Gamehelpofftherails|Off the Rails]] * [[Gamehelpohseven|Oh Seven]] * [[Gamehelponceuponaforest|Once Upon a Forest]] * [[Gamehelpone|ONE]] * [[Gamehelporiflamme|Oriflamme]] * [[Gamehelporigin|Origin]] * [[Gamehelpoutlaws|Outlaws:Last Man Standing]] * [[Gamehelppalace|Palace]] * [[Gamehelpcarrara|The Palaces of Carrara]] * [[Gamehelppandemic|Pandemic]] * [[Gamehelppapayoo|Papayoo]] * [[Gamehelpparisconnection|Paris Connection]] * [[Gamehelpsparts|Patahertta]] * [[Gamehelpspades|Patalupaus]] * [[Gamehelppedro|Pedro]] * [[Gamehelppenaltychallenge|Penalty Challenge]] * [[Gamehelppennypress|Penny Press]] * [[Gamehelpdudo|Perudo]] * [[Gamehelpphat|Phat]] * [[Gamehelppi|P.I.]] * [[Gamehelppingimus|Pingimus]] * [[Gamehelppiratenkapern|Piraten kapern]] * [[Gamehelppolis|Polis:Fight for Hegemony]] * [[Gamehelpponte|Ponte del Diavolo]] * [[Gamehelppotionexplosion|Potion Explosion]] * [[Gamehelppuertorico|Puerto Rico]] – [[Tips_puertorico|Vinkit]] * [[Gamehelppylos|Pylos]] * [[Gamehelpquarto|Quarto]] * [[Gamehelpquantik|Quantik]] * [[Gamehelpquantum|Quantum]] * [[Gamehelpquetzal|Quetzal]] * [[Gamehelpquinque|Quinque]] * [[Gamehelpquoridor|Quoridor]] * [[Gamehelpraceforthegalaxy|Race for the Galaxy]] * [[Gamehelprage|Rage]] * [[Gamehelprailroadink|Railroad Ink]] * [[Gamehelprallymangt|Rallyman: GT]] * [[Gamehelpfrenchtarot|Ranskalainen tarot]] * [[Gamehelpred7|Red 7]] * [[Gamehelprememberwhen|Remember When]] * [[Gamehelprestinpeace|Rest in Peace]] * [[Gamehelpreversi|Reversi]] * [[Gamehelprolledwest|Rolled West]] * [[Gamehelprollforthegalaxy|Roll for the Galaxy]] * [[Gamehelprussianrailroads|Russian Railroads]] * [[Gamehelptheruth|The Ruth: A Story of Coal Trade]] | * [[Gamehelpsaboteur|Saboteur]] * [[Gamehelpsaintpetersburg|Saint Petersburg]] * [[Gamehelpsaintpoker|Saint Poker]] * [[Gamehelpforbiddenisland|Salaisuuksien Saari]] * [[Gamehelpsantorini|Santorini]] * [[Gamehelpsapiens|Sapiens]] * [[Gamehelpschroedingerscats|Schrödinger's Cats]] * [[Gamehelpseasons|Seasons]] * [[Gamehelpsecretmoon|Secret Moon]] * [[Gamehelpthatslife|Sellaista sattuu!]] * [[Gamehelpsenet|Senet]] * [[Gamehelpchess|Shakki]] * [[Gamehelpsiam|Siam]] * [[Gamehelpsignorie|Signorie]] * [[Gamehelpskat|Skat]] * [[Gamehelpskull|Skull]] * [[Gamehelpsmallislands|Small Islands]] * [[Gamehelpsobek|Sobek]] * [[Gamehelpsobektwoplayers|Sobek 2 Players]] * [[Gamehelpsolarstorm|Solar Storm]] * [[Gamehelpsolo|Solo]] * [[Gamehelpsolowhist|Soolovisti]] * [[Gamehelpsoluna|Soluna]] * [[Gamehelpsplendor|Splendor]] * [[Gamehelpspyrium|Spyrium]] * [[Gamehelpsteamworks|Steam Works]] * [[Gamehelpstirfryeighteen|Stir Fry Eighteen]] * [[Gamehelpstoneage|Stone Age]] * [[Gamehelpsuperfantasybrawl|Super Fantasy Brawl]] * [[Gamehelpsushigo|Sushi Go]] * [[Gamehelptabult|Tabult]] * [[Gamehelptakaraisland|Takara Island]] * [[Gamehelptakenoko|Takenoko]] * [[Gamehelptaluva|Taluva]] * [[Gamehelpcheckers|Tammi]] * [[Gamehelptargi|Targi]] * [[Gamehelptashkalar|Tash-Kalar]] * [[Gamehelpteatime|Tea Time]] * [[Gamehelpteotihuacan|Teotihuacan: City of Gods]] * [[Gamehelpterramystica|Terra Mystica]] * [[Gamehelpthermopyles|Thermopyles]] * [[Gamehelpthroughtheages|Through the Ages]] * [[Gamehelpthurnandtaxis|Thurn und Taxis]] * [[Gamehelptichu|Tichu]] * [[Gamehelptiki|Tiki]] * [[Gamehelppatchwork|Tilkkutäkki]] * [[Gamehelptimemasters|Time Masters]] * [[Gamehelptoc|Tock]] * [[Gamehelptobago|Tobago]] * [[Gamehelptoeshambo|ToeShamBo]] * [[Gamehelptokaido|Tokaido]] * [[Gamehelptournay|Tournay]] * [[Gamehelptranquility|Tranquility]] * [[Gamehelptrektwelve|Trek 12]] * [[Gamehelptrekkingtheworld|Trekking the World]] * [[Gamehelptrickoftherails|Trick of the Rails]] * [[Gamehelptroyes|Troyes]] * [[Gamehelpturnthetide|Turn the Tide]] * [[Gamehelptwintinbots|Twin Tin Bots]] * [[Gamehelptzolkin|Tzolk'in]] * [[Gamehelpunclechestnuttablegype|Uncle Chesnut's Table Gype]] * [[Gamehelpunconditionalsurrender|Unconditional Surrender! World War 2 in Europe]] * [[Gamehelpunitedsquare|United Square]] * [[Gamehelphungariantarokk|Unkarilainen tarokk]] * [[Gamehelpveggiegarden|Veggie Garden]] * [[Gamehelpveletas|Veletas]] * [[Gamehelpveronatwist|Verona Twist]] * [[Gamehelpviamagica|Via Magica]] * [[Gamehelpwhisttwentytwo|Visti 22]] * [[Gamehelpviticulture|Viticulture]] * [[Gamehelpmarcopolo|The Voyages of Marco Polo]] * [[Gamehelpwelcometo|Welcome To]] * [[Gamehelpwelcometonewlasvegas|Welcome to New Las Vegas]] * [[Gamehelpthewerewolvesofmillershollow|The Werewolves of Miller's Hollow]] * [[Gamehelpwizard|Wizard]] * [[Gamehelpxiangqi|Xiangqi]] * [[Gamehelpyatzy|Yatzy]] * [[Gamehelpyinyang|Yin Yang]] * [[Gamehelpyokai|Yokai]] * [[Gamehelpyokohama|Yokohama]] |} === Beeta-pelit === * [[Gamehelpagricola|Agricola]] * [[Gamehelpcastleofburgundy|The Castles of Burgundy]] * [[Gamehelppresident|Orja]] * [[Gamehelpregicide|Regicide]] * [[Gamehelproomtwentyfive|Room Twentyfive]] 41671ac4cd06d2769e26e9a66a290385eb548378 Gamehelpabandonallartichokes 0 194 1210 2021-09-09T19:31:38Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki == Päämäärä == Nosta pakastasi käsi, jossa ei ole yhtään artisokkaa. == Pelin kulku == Pelaajat aloittavat pelin 10 artisokan pakalla, josta nostavat 5 korttia aloituskädeksi. === Vuoron vaiheet === # '''Täydennys''' - Täydennetään '''puutarharivi''' nostamalla '''puutarhapinosta''' kortteja kunnes rivissä on 5 korttia. # '''Sadonkorjuu''' - Pelaaja korjaa rivistä yhden kortin käteensä. # '''Pelaa''' - Pelaaja voi pelata kädestään kortteja. # '''Poista''' - Pelaaja vuoronsa lopuksi tyhjentää kätensä poistopinoonsa. # '''Nosto''' - Pelaaja nostaa 5 korttia nostopakasta käteensä ja seuraava pelaaja aloittaa vuoronsa. Nostopakan ehtyessä sekoitetaan poistopino uudeksi nostopakaksi. '''Huom!''' ''Jos et nosta yhtään artisokkaa nostovaiheessa, voitat pelin!'' == Vihannekset == === Artisokka === Et voi pelata artisokkaa ellei joku toinen kortti ohjeista tekemään niin. === Herneet === Paljasta kaksi korttia puutarhapinon päältä. Laita yksi kortti sinun ja toisen pelaajan poistopinon päälle. Jos puutarhapinossa ei ole kortteja jäljellä, et voi pelata tätä korttia. === Maissi === Pelaa tämä kortti artisokan kanssa. Laita sitten kortti '''puutarhariviltä''' nostopinosi päälle. === Munakoiso === Kompostoi artisokka tämän kortin lisäksi. Sitten kaikki pelaajat antavat vasemmalleen kaksi omavalintaista korttia. Jos pelaajalla on vähemmän kuin kaksi korttia, he antavat sen, minkä kykenevät. === Paprika === Laita kortti poistopinostasi nostopinon päälle. === Parsakaali === Kompostoi artisokka kädestäsi jos kädessäsi on kolme tai enemmän artisokkaa. === Peruna === Paljasta nostopakkasi päällimmäinen kortti. Jos se on artisokka, kompostoi se. Muuten laita se poistopinosi päälle. === Porkkana === Vuorosi ainoana toimintana kompostoi tasan kaksi artisokkaa tämän kortin lisäksi. === Punajuuri === Valitse toinen pelaaja. Kummatkin nostavat sokeana toistensa käsistään kortin. Jos kummatkin ovat artisokkia, ne menevät kompostiin, muutoin kortit vaihtuvat pelaajien kesken. Jos saat kortin, voit pelata sen samalla vuorolla. Jos vastustajallasi on vähemmän kuin 5 korttia kompostoinnin jälkeen, hän '''ei''' nosta viiteen korttiin kuin vasta vuoronsa lopussa. === Purjo === Paljasta toisen pelaajan nostopakan päällimmäinen kortti, ja laita se joko hänen poistopinon päällimmäiseksi tai omaan käteesi. === Sipuli === Kompostoi artisokka. Laita tämä kortti toisen pelaajan poistopinoon. === Raparperi (promootio) === Kompostoi tämä kortti täydentääksesi koko '''puutarharivin''', sitten korjaa kortti. (Sijoita vanhemmat kortit puutarhapinon alle.) 1a4766663a2a74cb3afc4e94ee3f0b5e03799863 Muistiinpanoja 0 195 1214 2021-09-13T13:01:39Z Rexroom 5688 Ak: Uusi sivu: * [[Lokalisointi/Raceforthegalaxy|Race for the Galaxy]] wikitext text/x-wiki * [[Lokalisointi/Raceforthegalaxy|Race for the Galaxy]] cedb74f5ab7877e6a6a6d70718ad06af66b3c4d3 Lokalisointi/Raceforthegalaxy 0 196 1215 2021-09-13T14:21:03Z Rexroom 5688 Ak: Uusi sivu: == Termistö == === Avainsanasto === Race for the Galaxy sisältää useita avainsanoja, joita on hyvä pitää yhtenäisenä eri korttien välillä. ==== Peruspeli ==== ;£Alie... wikitext text/x-wiki == Termistö == === Avainsanasto === Race for the Galaxy sisältää useita avainsanoja, joita on hyvä pitää yhtenäisenä eri korttien välillä. ==== Peruspeli ==== ;£Alien£ :muukalais, ulkoavaruuden olento/olio ;+Imperium+ :imperiumi ;!Rebel! :kapina-, kapinallinen ;€Terraforming€ :maankaltaistaa ;Uplift :kohotus (kts. [[wikipedia:fi:Tähtisumu täyttyy]]) ;@Xeno@ :kseno ;Good :hyödyke ;Military :sotavoimat ;Mining :kaivostyö, louhinta ;Novelty :lahjatavara ;Rare elements :(harvinaiset) alkuaineet ;Windfall :"Windfall" (kts. [[wikipedia:fi:Windfall-voitto]]) === Xeno-laajennos === ;^ANTi-XENO^ :antikseno === Orb-laajennos === ;Orb :kuula bdf9f8f0183aee7559e01f9f4dc5dbe8ae712f7c Gamehelpalhambra 0 176 1216 1165 2021-09-24T11:40:40Z MikaJK 5905 wikitext text/x-wiki Voit tehdä yhden toiminnon vuorosi aikana: * Nostaa rahaa (joko yhden tai enintään 5 rahan edestä kortteja) * Ostaa rakennuslaatan * Järjestellä Alhambraasi uudestaan Jos ostat rakennuslaatan tasarahalla, saat suorittaa ylimääräisen toiminnon. Vuorosi lopussa aseta ostamasi rakennuslaatat Alhambraasi. ea2d426bf4c0a460a9dadcf6d48ca92ddbadddfd Gamehelpforex 0 197 1217 2021-10-04T16:30:01Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Peli valuuttakaupustelijoista 2–6 pelaajalle. == Pelin päämäärä == Pelaaja, jolla on eniten rahaa pelin lopussa, voittaa pelin. == Pelin kulku == Pelaajat valitsevat vuorollaan vain yhden seuraavista toiminnoista: * Tee sopimus pankin kanssa * Investoi kahteen valuuttaan * Divestoi yhdestä valuutasta * Päätä sopimus, laina tai osingonjako Pelaajat voivat myös ennen toimintoaan tehdä spot-kauppaa toisen pelaajan kanssa. === Spot-kauppa === Pelaaja tekee tarjouksen valuuttojen vaihtokaupasta käyttäen vallitsevia kurssisuhdanteita. Tätä toimintoa voi käyttää kerran vuorosi alussa, eikä läsketa toiminnoksi. === Sopimus === Pelaaja valitsee yhden vapaana olevan sopimuksen, asettaa sen viimeiseksi sopimusjonoon ja määrittelee, miten paljon vaihtaa valuutasta A valuuttaan B. Voit maksaa vahvimmalla valuutalla korkeintaan 10. === Investointi === Osta yksi tai kaksi eri valuuttaa 2 rahayksiköllä. Sinulla voi olla korkeintaan neljä valuuttasertifikaattia. Ostamasi valuutat samalla '''vahvistuvat'''. === Divestointi === Myy mikä tahansa määrä yhden valuutan sertifikaatteja, poistaen ne pelistä ja saaden 2 rahayksikkö jokaisesta myydystä sertifikaatista. Muut pelaajat voivat vuorollaan myös myydä samaa valuuttaa pois samaan hintaan. Divestoitu valuutta sitten '''heikkenee''' myytyjen sertifikaattien verran. === Päätä sopimus === Päätä etummaisin kortti sopimusjonossa. Jos kyseessä on sopimus, kyseinen pelaaja maksaa sopimuksessa olevan verran rahaa ja saa vaihtorahansa. Jos pelaaja ei kykene maksamaan, vaihda se lainaksi. Jos kyseessä on laina, pelaajan on maksettava lainassa olevat määrät. Jos hän ei kykene, hän ajautuu '''konkurssiin''' ja peli päättyy. Jos kyseessä on osingot, pelaajat saavat merkityn verran valuuttoja jokaisesta omistetusta valuuttasertifikaatista, ''paitsi niiden valuuttojen kohdalla, joiden kurssi on ruudulla 8''. Osinkojen maksun jälkeen se valuutta, jonka sertifikaatteja omistetaan eniten, '''vahvistuu'''. Tasatilanteen tapauksessa sopimuksen päättäjä saa valita vahvimman valuutan. '''Huom!''' Kun viimeinen osinko on pelattu, loputkin sopimusjonon korteistä selvitetään vuorotellen. Sitten peli päättyy. == Pelin loppu == Peli päättyy kahdella tavalla: joku pelaajista menee konkurssiin tai viimeinen osingonjako on suoritettu. Kun viimeinen osingonjako on suoritettu, selvitä loputkin kortit sopimusjonossa. '''Huom!''' Jos peli päättyy konkurssiin, loput sopimusjonon korteista '''ei''' selvitetä. Kaikki valuutat vaihdetaan vahvimpaan, pyöristäen alas. Mahdollisen tasatilanteen ratkaisee omistettujen sertifikaattien suurin määrä, lopullisena ratkaisijana sitten viimeisenä vuoronsa pelannut pelaaja. Eniten rahaa tuottanut voittaa pelin. 0375f1190e365255a83ff3aa85eb0663981acb91 1218 1217 2021-10-04T16:31:50Z Rexroom 5688 /* Spot-kauppa */ typo fix wikitext text/x-wiki Peli valuuttakaupustelijoista 2–6 pelaajalle. == Pelin päämäärä == Pelaaja, jolla on eniten rahaa pelin lopussa, voittaa pelin. == Pelin kulku == Pelaajat valitsevat vuorollaan vain yhden seuraavista toiminnoista: * Tee sopimus pankin kanssa * Investoi kahteen valuuttaan * Divestoi yhdestä valuutasta * Päätä sopimus, laina tai osingonjako Pelaajat voivat myös ennen toimintoaan tehdä spot-kauppaa toisen pelaajan kanssa. === Spot-kauppa === Pelaaja tekee tarjouksen valuuttojen vaihtokaupasta käyttäen vallitsevia kurssisuhdanteita. Tätä toimintoa voi käyttää kerran vuorosi alussa, eikä lasketa toiminnoksi. === Sopimus === Pelaaja valitsee yhden vapaana olevan sopimuksen, asettaa sen viimeiseksi sopimusjonoon ja määrittelee, miten paljon vaihtaa valuutasta A valuuttaan B. Voit maksaa vahvimmalla valuutalla korkeintaan 10. === Investointi === Osta yksi tai kaksi eri valuuttaa 2 rahayksiköllä. Sinulla voi olla korkeintaan neljä valuuttasertifikaattia. Ostamasi valuutat samalla '''vahvistuvat'''. === Divestointi === Myy mikä tahansa määrä yhden valuutan sertifikaatteja, poistaen ne pelistä ja saaden 2 rahayksikkö jokaisesta myydystä sertifikaatista. Muut pelaajat voivat vuorollaan myös myydä samaa valuuttaa pois samaan hintaan. Divestoitu valuutta sitten '''heikkenee''' myytyjen sertifikaattien verran. === Päätä sopimus === Päätä etummaisin kortti sopimusjonossa. Jos kyseessä on sopimus, kyseinen pelaaja maksaa sopimuksessa olevan verran rahaa ja saa vaihtorahansa. Jos pelaaja ei kykene maksamaan, vaihda se lainaksi. Jos kyseessä on laina, pelaajan on maksettava lainassa olevat määrät. Jos hän ei kykene, hän ajautuu '''konkurssiin''' ja peli päättyy. Jos kyseessä on osingot, pelaajat saavat merkityn verran valuuttoja jokaisesta omistetusta valuuttasertifikaatista, ''paitsi niiden valuuttojen kohdalla, joiden kurssi on ruudulla 8''. Osinkojen maksun jälkeen se valuutta, jonka sertifikaatteja omistetaan eniten, '''vahvistuu'''. Tasatilanteen tapauksessa sopimuksen päättäjä saa valita vahvimman valuutan. '''Huom!''' Kun viimeinen osinko on pelattu, loputkin sopimusjonon korteistä selvitetään vuorotellen. Sitten peli päättyy. == Pelin loppu == Peli päättyy kahdella tavalla: joku pelaajista menee konkurssiin tai viimeinen osingonjako on suoritettu. Kun viimeinen osingonjako on suoritettu, selvitä loputkin kortit sopimusjonossa. '''Huom!''' Jos peli päättyy konkurssiin, loput sopimusjonon korteista '''ei''' selvitetä. Kaikki valuutat vaihdetaan vahvimpaan, pyöristäen alas. Mahdollisen tasatilanteen ratkaisee omistettujen sertifikaattien suurin määrä, lopullisena ratkaisijana sitten viimeisenä vuoronsa pelannut pelaaja. Eniten rahaa tuottanut voittaa pelin. 1f68f175e97484ed2664a86720569d08674896d7 Sanakirja 0 171 1219 1196 2021-10-27T19:33:03Z Rexroom 5688 /* B */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä ;action :toiminto, toiminta === B === ;bear off :''backgammon'' ulosheitto ;bid :tarjota jstn ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin ;face down :(naama/etu-/kuvapuoli) alaspäin ;first player :''lautapeli'' aloituspelaaja ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 0b7bd092ec5f3582131a90b2cabc034f22d0ef47 1220 1219 2021-10-27T20:12:45Z Rexroom 5688 /* F */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä ;action :toiminto, toiminta === B === ;bear off :''backgammon'' ulosheitto ;bid :tarjota jstn ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin, pöydätty ;face down :(naama/etu-/kuvapuoli) alaspäin ;first player :''lautapeli'' aloituspelaaja ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 4962635c5c030c574393d9d1de187c953e8712b8 1221 1220 2021-11-01T12:00:12Z Rexroom 5688 /* Sanasto */ wikitext text/x-wiki Suomennoksia yleisesti käytetyistä termeistä. == Sanasto == === A === ;ace :''kortti'' ässä ;action :toiminto, toiminta === B === ;bear off :''backgammon'' ulosheitto ;bid :tarjota jstn ;blot :''backgammon'' blotti (yksinäinen nappula) ;burn :''kortti'' kaato === C === ;card :kortti ;check :''shakki'' shakki ::''check and mate'' shakki ja matti ;club :''kortti'' risti ;column :pystyrivi, sarake === D === ;dead :kuollut ;dealer :jakaja, yleensä ''kortit'' ;deck :''kortti'' pakka ;diamond :''kortti'' ruutu ;dice :nopat (mon.) ;die :noppa (yks.) :kuolla ;disabled :''atk'' ei käytössä ;discard :poisto ;draw :tasapeli :''kortti'' nosta (kortti) :piirrä === E === ;enabled :''atk'' käytössä === F === ;face up :(naama/etu-/kuvapuoli) ylöspäin, pöydätty ;face down :(naama/etu-/kuvapuoli) alaspäin ;first player :''lautapeli'' aloituspelaaja ;flush :''pokeri'' väri ;four of a kind :''pokeri'' neljä samaa ;full house :''pokeri'' täyskäsi === G === ;game :peli :riista ;group :ryhmä === H === ;hearts :''kortti'' hertta ;high :''pokeri'' hai, korkein kortti === I === === J === ;jack :''kortti'' jätkä, soturi === K === ;king :''kortti'' kuningas === L === === M === === N === === O === === P === ;pass :ohittaa (vuoro), passaaminen ;phase :vaihe ;play :pelikerta :''verbi'' pelaa, pelata jtn ;play a card :''kortti'' pelaa kortti, pöytää kortti ;player :pelaaja === Q === ;queen :''kortti'' kuningatar === R === ;roll :''noppa'' heitä (noppaa) ;round :kierros ;row :vaakarivi, rivi === S === ;shuffle :sekoittaa (korttipakka) ;spade :''kortti'' pata ;straight :suora ;straight flush :''pokeri'' värisuora ;suit :''kortti'' maa === T === ;team :joukkue ;three of a kind :''pokeri'' kolme samaa ;tie :tasapeli ;token :pelimerkki, poletti, rahake ;tooltips :''atk'' työkaluvinkit ;trick :''kortti'' tikki ;trump :''kortti'' valtti(kortti) ;turn :vuoro ;turn order :vuorojärjestys ;two pairs :''pokeri'' kaksi paria === U === === V === ;victory point :voittopiste === W === ;worker :työläinen === X === === Y === === Z === 04a2dd194752de65a51d4f6c6ec7aa5d8d1841fb Gamehelpcribbage 0 198 1222 2021-11-16T21:45:48Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Kahden pelaajan yhteenlasku- ja yhdistelmäkorttipeli. Ensimmäinen, joka kerää 121 pistettä, voittaa pelin. == Korttien arvot == Korttien numerot vastaavat arvoa, jossa ässä on 1 arvoinen ja kuvakortit 10. == Pisteyhdistelmät == {| |15 |2 p. |Korttiyhdistelmä, jonka summa on 15. |- |Pari |2 p. |Korttipari. |- |Kolmiluku (kuningaspari) |6 p. |Kolme samaa korttia. |- |Neliluku (tuplakuningaspari) |12 p. |Neljä samaa korttia. |- |Suora |1 p. / kortti |Peräkkäisiä kortteja, vähintään kolme. |- |Väri |1 p. / kortti |Samaa maata. ''Lasketaan vain näytössä.'' |} == Pelin kulku == Peli alkaa pelaajien nostamalla kortin pakasta. Matalimman nostanut on aloitusjakaja. Tästä eteenpäin jakaja vaihtuu kierroksen jälkeen vuorotellen pelaajien kesken. === Jako === Pelaajille jaetaan 6 korttia, joista kaksi sijoitetaan kuvapuoli alaspäin "cribiin", joka kuuluu jakajalle. Pakasta nostetaan aloituskortti. Jos se on sotilas, jakaja saa heti 2 pistettä. ("Kaksi kannuksista.") === Peli === Etukäsi aloittaa. Pelaajat vuorotellen pöytäävät kortin kädestään. Jos pelaaja ei pysty pelaamaan korttia ilman, että pöydättyjen korttien summa menee yli 31, hänen on passattava ja vastustaja saa 1 pisteen. Jos pelaajilla on vielä kortteja, pöydätyt kortit pistetään sivuun ja aloitetaan taas nollasta. Viimeisen kortin pelannut saa 1 pisteen. === Näyttö === Kun kortit on pelattu, katsotaan pelattuja käsiä yhdessä aloituskortin kanssa. Pelaajat pisteyttävät kätensä. Jos pelaajalla on kädessään sotilas, joka on samaa maata kuin aloituskortti, hän saa "päänupista" heti 2 pistettä. Sitten jakaja pisteyttää samalla tavansa cribinsä. Kierros päättyy. == Pelin päättyminen == Peli loppuu heti, kun joku pelaajista saa määrätyn määrän pisteitä. 7590cba54fb7e8962ba4426605f8c83e177b0046 1235 1222 2022-07-20T10:07:52Z Rexroom 5688 /* Näyttö */ typo wikitext text/x-wiki Kahden pelaajan yhteenlasku- ja yhdistelmäkorttipeli. Ensimmäinen, joka kerää 121 pistettä, voittaa pelin. == Korttien arvot == Korttien numerot vastaavat arvoa, jossa ässä on 1 arvoinen ja kuvakortit 10. == Pisteyhdistelmät == {| |15 |2 p. |Korttiyhdistelmä, jonka summa on 15. |- |Pari |2 p. |Korttipari. |- |Kolmiluku (kuningaspari) |6 p. |Kolme samaa korttia. |- |Neliluku (tuplakuningaspari) |12 p. |Neljä samaa korttia. |- |Suora |1 p. / kortti |Peräkkäisiä kortteja, vähintään kolme. |- |Väri |1 p. / kortti |Samaa maata. ''Lasketaan vain näytössä.'' |} == Pelin kulku == Peli alkaa pelaajien nostamalla kortin pakasta. Matalimman nostanut on aloitusjakaja. Tästä eteenpäin jakaja vaihtuu kierroksen jälkeen vuorotellen pelaajien kesken. === Jako === Pelaajille jaetaan 6 korttia, joista kaksi sijoitetaan kuvapuoli alaspäin "cribiin", joka kuuluu jakajalle. Pakasta nostetaan aloituskortti. Jos se on sotilas, jakaja saa heti 2 pistettä. ("Kaksi kannuksista.") === Peli === Etukäsi aloittaa. Pelaajat vuorotellen pöytäävät kortin kädestään. Jos pelaaja ei pysty pelaamaan korttia ilman, että pöydättyjen korttien summa menee yli 31, hänen on passattava ja vastustaja saa 1 pisteen. Jos pelaajilla on vielä kortteja, pöydätyt kortit pistetään sivuun ja aloitetaan taas nollasta. Viimeisen kortin pelannut saa 1 pisteen. === Näyttö === Kun kortit on pelattu, katsotaan pelattuja käsiä yhdessä aloituskortin kanssa. Pelaajat pisteyttävät kätensä. Jos pelaajalla on kädessään sotilas, joka on samaa maata kuin aloituskortti, hän saa "päänupista" heti 2 pistettä. Sitten jakaja pisteyttää samalla tavalla cribinsä. Kierros päättyy. == Pelin päättyminen == Peli loppuu heti, kun joku pelaajista saa määrätyn määrän pisteitä. 75474b4a6f373b5a2ad0788f2d519e9f982cd42e Gamehelpelgrande 0 199 1224 2022-02-01T15:25:46Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == ;alue :Pelilaudassa näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia ;caballero :Pienaatelinen, asetetaan pelilaudan alueisiin ;castillo :Alueesta riippumaton linnake, johon voi asettaa caballerojaan alueiden sijaan ;grandi :Pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa ;hovi :Varanto, mistä caballerot sijoitetaan alueisiin ;kuninkaan alue :Alue, jossa on kuninkaan pelimerkki ;provinssi :Pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä ;toimintokortit :kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa kierroksen aikana caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja ;voimakortit :Arvoltaan 1–13 olevia kortteja, jotka määräävät == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 3 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä voimakortissaan näkyvän caballero-ikonien määrän provinssista hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on koskematon, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillo caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. d8d4a0d668167378f2c51f7a21e015705a2206ee 1225 1224 2022-02-01T15:32:20Z Rexroom 5688 /* Pelikierroksen kulku */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == ;alue :Pelilaudassa näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia ;caballero :Pienaatelinen, asetetaan pelilaudan alueisiin ;castillo :Alueesta riippumaton linnake, johon voi asettaa caballerojaan alueiden sijaan ;grandi :Pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa ;hovi :Varanto, mistä caballerot sijoitetaan alueisiin ;kuninkaan alue :Alue, jossa on kuninkaan pelimerkki ;provinssi :Pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä ;toimintokortit :kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa kierroksen aikana caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja ;voimakortit :Arvoltaan 1–13 olevia kortteja, jotka määräävät == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 3 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista voimakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on koskematon, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillo caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. 4898961774d2eec8d53fb0a05c489d72897affe1 1226 1225 2022-02-01T15:46:08Z Rexroom 5688 /* Termistö */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == '''alue''' – pelilaudalla näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia '''caballero''' – pienaatelinen, asetetaan pelilaudan alueisiin pisteytystä varten '''castillo''' – alueesta riippumaton linnake, johon voi pudottaa caballerojaan alueiden sijaan '''grandi''' – pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa '''hovi''' – varanto, josta caballerot siirretään alueisiin '''kuninkaan alue''' – alue, jossa on kuninkaan pelimerkki '''provinssi''' – pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä '''toimintokortit''' – kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja kierroksen aikana '''voimakortit''' – kortteja, jotka määräävät pelaajien vuorojärjestyksen ja provinssista hoviin siirrettävien caballerojen määrän == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 3 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista voimakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on koskematon, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillo caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. 649a26dadc16a34275237278939f88a08f55e4d8 1227 1226 2022-02-02T22:27:07Z Rexroom 5688 /* Pelin kulku */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == '''alue''' – pelilaudalla näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia '''caballero''' – pienaatelinen, asetetaan pelilaudan alueisiin pisteytystä varten '''castillo''' – alueesta riippumaton linnake, johon voi pudottaa caballerojaan alueiden sijaan '''grandi''' – pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa '''hovi''' – varanto, josta caballerot siirretään alueisiin '''kuninkaan alue''' – alue, jossa on kuninkaan pelimerkki '''provinssi''' – pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä '''toimintokortit''' – kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja kierroksen aikana '''voimakortit''' – kortteja, jotka määräävät pelaajien vuorojärjestyksen ja provinssista hoviin siirrettävien caballerojen määrän == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 2 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista voimakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on koskematon, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillo caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. fca7126a0d9cfffae6645569fd774a3d3f40a3ab 1228 1227 2022-02-02T22:33:09Z Rexroom 5688 /* Pelin kulku */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == '''alue''' – pelilaudalla näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia '''caballero''' – pienaatelinen, asetetaan pelilaudan alueisiin pisteytystä varten '''castillo''' – alueesta riippumaton linnake, johon voi pudottaa caballerojaan alueiden sijaan '''grandi''' – pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa '''hovi''' – varanto, josta caballerot siirretään alueisiin '''kuninkaan alue''' – alue, jossa on kuninkaan pelimerkki '''provinssi''' – pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä '''toimintokortit''' – kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja kierroksen aikana '''voimakortit''' – kortteja, jotka määräävät pelaajien vuorojärjestyksen ja provinssista hoviin siirrettävien caballerojen määrän == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 2 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista voimakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on tabu, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen, mistä tulee uusi kuninkaan alue. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillon caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. Muista sijoista ei saa pisteitä. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Heitä toisena oleva saisi 3. sijan pisteet eli 1 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. 03aa074791f7802c80b6683b85a2f6e55226deb5 1229 1228 2022-02-04T17:40:32Z Rexroom 5688 /* Termistö */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == '''alue''' – pelilaudalla näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia '''caballero''' – pienaatelinen, asetetaan pelilaudan alueisiin pisteytystä varten '''castillo''' – alueesta riippumaton linnake, johon voi pudottaa caballerojaan alueiden sijaan '''grandi''' – pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa '''hovi''' – varanto, josta caballerot siirretään alueisiin '''kuninkaan alue''' – alue, jossa on kuninkaan pelimerkki '''provinssi''' – pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä '''toimintokortit''' – kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja kierroksen aikana '''valtakortit''' – kortteja, jotka määräävät pelaajien vuorojärjestyksen ja provinssista hoviin siirrettävien caballerojen määrän == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 2 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään voimakortin. Pelaajat pelaavat vuoronsa pöydättyjen voimakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista voimakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on tabu, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen, mistä tulee uusi kuninkaan alue. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillon caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. Muista sijoista ei saa pisteitä. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Heitä toisena oleva saisi 3. sijan pisteet eli 1 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. 3c475ef803b74dbef09de3a2b8a1ab8f15be0051 1230 1229 2022-02-04T17:54:53Z Rexroom 5688 /* Pelin kulku */ wikitext text/x-wiki Pelaajat toimivat grandeina, jotka yrittävät haalia mahdollisimman paljon vaikutusvaltaa Espanjan kuningaskunnan ympäristöstä. == Termistö == '''alue''' – pelilaudalla näkyvät Espanjan kuningaskunnan maa-alueet, esim. Valencia '''caballero''' – pienaatelinen, asetetaan pelilaudan alueisiin pisteytystä varten '''castillo''' – alueesta riippumaton linnake, johon voi pudottaa caballerojaan alueiden sijaan '''grandi''' – pelaajan aatelinen, ei lasketa mukaan pisteytysvaiheissa '''hovi''' – varanto, josta caballerot siirretään alueisiin '''kuninkaan alue''' – alue, jossa on kuninkaan pelimerkki '''provinssi''' – pelaajien varanto caballeroja, jotka odottavat hoviin pääsyä '''toimintokortit''' – kierroksen 5 valittavaa korttia, millä pelaajat voivat laittaa caballerojaan hovista pelialueelle ja tehdä erikoistoimintoja kierroksen aikana '''valtakortit''' – kortteja, jotka määräävät pelaajien vuorojärjestyksen ja provinssista hoviin siirrettävien caballerojen määrän == Pelin kulku == Alussa pelaajien kotialueet arvotaan. Niihin asetetaan pelaajien grandi ja 2 caballeroa. === Pelikierroksen kulku === Kierroksen viisi toimintokorttia paljastetaan. Edellisessä kierroksessa viimeisenä pelaajana vuoronsa päättänyt aloittaa. (Ensimmäisessä kierroksessa aloituspelaaja on arvottu.) Pelaajat pöytäävät myötäpäivää vuorotellen kädestään valtakortin. Pelaajat pelaavat vuoronsa pöydättyjen valtakorttien mukaisessa järjestyksessä, alkaen suurimmasta. Pelaaja aloittaa vuoronsa siirtämällä provinssista valtakortissaan näkyvän caballero-ikonien määrän verran caballeroja hoviinsa. Sitten hän valitsee yhden vapaista toimintokorteista. Hän voi nyt tehdä kaksi toimenpidettä vapaassa järjestyksessä: (a) toteuttaa valitsemansa toimintokortin tekstin ja (b) asettaa valitsemansa toimintokortin ikonien määrän verran caballeroja pelilaudalle. (a) Toimintokortin tekstin toteuttaminen on vapaaehtoista – voit ottaa kortin ilman, että sinun pitäisi käyttää sitä. (b) Caballerot asetetaan hovista pelilaudan kuninkaan alueen viereisille alueille. Voit myös laittaa ne castilloon. Asetettavia caballeroja voi jakaa eri alueisiin tai vain yhteen. '''Huom!''' Kuninkaan alue on tabu, eli pelaajat eivät voi muokata sitä millään tavalla muuten kuin siirtämällä kuningasta toimintokortilla uuteen alueeseen, mistä tulee uusi kuninkaan alue. === Pisteytysvaihe === 3., 6. ja 9. kierroksen jälkeen toteutetaan pisteytysvaihe. Jokainen pelaaja valitsee salaa alueen. Castillon caballerot paljastetaan ja pisteytetään, jonka jälkeen castillon caballerot siirretään pelaajien valitsemiinsa alueisiin. Sen jälkeen alkaa alueiden pisteytys. Alueiden ja castillon pisteytys toimii enemmistömäärällä. Pelaaja, jolla on eniten caballeroja alueella, saa pistelaatan 1. sijan pistemäärän, toiseksi eniten 2. sijan pistemäärän ja kolmanneksi eniten 3. sijan pistemäärän. Muista sijoista ei saa pisteitä. '''Huom!''' Kolminpelissä vain kaksi ensimmäistä pistemäärää lasketaan, kun taas kaksinpelissä vain ensimmäinen. '''Huom!''' Tasatilanteessa olevat pelaajat ottavat askeleen pienemmän pistemäärän. Toisin sanoen, jos alueen pistelaatassa on "5-3-1" ja kolmella pelaajalla on yhtä paljon caballeroja alueessa, he saisivat kolmistaan vain 2. sijan pistemäärän eli 3 pistettä. Heitä toisena oleva saisi 3. sijan pisteet eli 1 pistettä. Kaksin- ja kolminpelien pistesäännöt pätevät tässäkin. Jos pelaajalla on enemmistö caballeroja kuninkaan alueella, hän saa kuninkaan bonuksen eli 2 pistettä. Jos pelaajalla on enemmistö caballeroja grandinsa alueella, hän saa kotialuebonuksen eli 2 pistettä. ''Huomaa, ettei grandia lasketa mukaan, kun tarkistetaan enemmistöja.'' == Pelin päättyminen == Peli päättyy, kun 3. pisteytysvaihe on suoritettu. Pelaaja, joka on kerännyt eniten pisteitä, voittaa pelin. Tasapelissä pelaajat jakavat voiton. 0fec6db09798a0103d739fb478c68ccec349d63c Gamehelpregicide 0 200 1233 2022-07-20T10:05:09Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Yhteistyöpeli 1–4 pelaajalle, jossa pelaajat yrittävät kukistaa voimakkaita vihollisia peruskorttipakalla. == Alkuasettelu == Kuninkaallisista rakennetaan linnapakka, josta nostetaan vihollisia. Ensiksi nostetaan jätkät, sitten kuningattaret ja lopulta kuninkaat. Loput kortit ja mahdolliset hovinarri-kortit sekoitetaan tavernapakaksi, josta pelaajat nostavat alussa kortteja käsikoon verran. {| class="wikitable" style="margin:auto; text-align:center" ! Pelaajia !! Hovinarreja !! Käsikoko |- | 1 || 0 || 8 |- | 2 || 0 || 7 |- | 3 || 1 || 6 |- | 4 || 2 || 5 |} == Viholliset == {| class="wikitable" style="margin:auto; text-align:center" ! Viholliset !! Terveys !! Hyökkäys |- | Jätkät || 20 || 10 |- | Kuningattaret || 30 || 15 |- | Kuninkaat || 40 || 20 |} Kukistetut viholliset pelaajan kädessä toimivat kuin normaalit kortit, mutta niiden arvot ovat listauksen hyökkäysten arvoisia. == Ei suoraa viestintää! == Koska kyseessä on yhteistyöpeli, '''pelaajat eivät saa puhua keskenään mistään muusta kuin julkisesta tiedosta pelin aikana!''' Tämä tarkoittaa, ettet voi esim. kertoa kätesi sisällöstä, mitä aiot tehdä tai mitä muiden kannattaisi tehdä. Korttien määristä ja muista pelialueen kohteista voi puhua. == Pelin kierroksen vaiheet == Pelaajat pelaavat yksitellen vuoronsa. Vuorolla on 4 vaihetta: # Pelaa kortti/kortteja tai peräänny # Aktivoi pelatut maakyvyt # Tee vahinkoa viholliselle ja tarkista # Ota vastaan vahinkoa viholliselta Vaiheitten jälkeen alkaa seuraavan pelaajan vuoro. === Pelaa kortteja tai peräänny === Pelaaja voi pelata kädestään kortteja seuraavanlaisesti: * Yksittäisen kortin * Yksittäisen kortin eläinkumppani-kortin kanssa (ässät) * Sarjan kortteja (kombot) ==== Eläinkumppanit ==== Eläinkumppanikortit (eli ässät) voidaan pelata parina toisen kortin kanssa. Ässät lasketaan 1 arvoisiksi. ==== Kombot ==== Samanarvoisia kortteja voi pelata yhdessä, mutta niiden summa ei saa ylittää 10. ==== Perääntyminen ==== Pelaaja voi myös '''perääntyä''', jolloin hän ei pelaa kädestään kortteja, mutta ottaa silti vahinkoa viholliselta. '''Huom!''' Kierroksen aikana vähintään yhden pelaajan on pelattava kortteja, joten jos muut pelaajat ovat perääntyneet omalla vuorollaan, viimeisen pelaajan on '''pakko''' pelata korttejaan. === Maakykyjen aktivointi === Neljällä maalla on omat kykynsä, jotka auttavat pelaajia: * Pata – suojaa pelaajia vähentämällä pelatun kokonaisarvon verran vihollisen hyökkäystä * Risti – tuplaa pelaajan hyökkäysvoimaa * Hertta – sekoittaa poistopinon, ottaa kokonaisarvon verran kortteja ja siirtää ne tavernapakan alle * Ruutu – nostaa kokonaisarvon verran kortteja tavernapakasta; jokainen pelaaja nostaa vuorotellen yhden kortin pakasta, aloittaen kyseisen vuoron pelaajasta Jos hertta ja ruutu pelataan samaan aikaan, hertta suoritetaan ensimmäisenä. Tietyn maan kykyä aktivoidaan vain kerran. ==== Vihollisten maasuoja ==== Jos kortilla on sama maa kuin vihollisen kortilla, vihollisella on suoja sitä vastaan. Tämä tarkoittaa, ettei kyseinen kyky aktivoidu tässä vaiheessa. Pelaaja voi poistaa suojan '''moninpelissä''' pelaamalla hovinarrin. Patojen kohdalla ennestään pelatut patakortit aktivoituvat. === Tee vahinkoa viholliselle === Pelaaja tekee pelattujen korttien verran vahinkoa nykyiselle viholliselle. Jos pelaajien pelaamien vahingon määrä ylittää vihollisen terveyden, hänet kukistetaan ja asetetaan poistopinoon vihollisen aikana pelattujen korttien mukana. Linnapakasta käännetään uusi vihollinen, ja pelaajan vuoro alkaa alusta. '''Huom!''' Jos pelaajat tekevät tasan saman verran vahinkoa kuin vihollisen terveys, kukistettu vihollinen asetetaan poistopinon sijaan tavernapakan päällimmäiseksi kortiksi. === Vihollinen hyökkää === Vihollinen hyökkää vuoroaan pelaavaa pelaajaa vastaan. Hänen on poistettava kädestään vähintään vihollisen hyökkäysvoiman verran arvoisia kortteja poistopinoon. '''Jos pelaaja ei pysty poistamaan tarpeeksi kortteja kädestään, peli päättyy pelaajien häviöön!''' Hyökkäyksen jälkeen vuoro siirtyy seuraavalle pelaajalle. == Pelin päättyminen == Pelaajat voittavat, jos viimeinen kuningas kukistetaan. Jos pelaaja ei pysty poistamaan tarpeeksi kortteja hyökkäyksen takia, pelaajat häviävät. Pelaajat voivat myös hävitä, jos pelaaja ei pysty vuorollaan pelaamaan korttia tai perääntymään. ebcc17f90778a2e577544265610445cd229b496a 1236 1233 2022-07-20T10:16:46Z Rexroom 5688 wikitext text/x-wiki Yhteistyöpeli 1–4 pelaajalle, jossa pelaajat yrittävät kukistaa voimakkaita vihollisia peruskorttipakalla. == Alkuasettelu == Kuninkaallisista rakennetaan linnapakka, josta nostetaan vihollisia. Ensiksi nostetaan jätkät, sitten kuningattaret ja lopulta kuninkaat. Loput kortit ja mahdolliset hovinarri-kortit sekoitetaan tavernapakaksi, josta pelaajat nostavat alussa kortteja käsikoon verran. {| class="wikitable" style="margin:auto; text-align:center" ! Pelaajia !! Hovinarreja !! Käsikoko |- | 1 || 0 || 8 |- | 2 || 0 || 7 |- | 3 || 1 || 6 |- | 4 || 2 || 5 |} == Viholliset == {| class="wikitable" style="margin:auto; text-align:center" ! Viholliset !! Terveys !! Hyökkäys |- | Jätkät || 20 || 10 |- | Kuningattaret || 30 || 15 |- | Kuninkaat || 40 || 20 |} Kukistetut viholliset pelaajan kädessä toimivat kuin normaalit kortit, mutta niiden arvot ovat listauksen hyökkäysten arvoisia. == Ei suoraa viestintää! == Koska kyseessä on yhteistyöpeli, '''pelaajat eivät saa puhua keskenään mistään muusta kuin julkisesta tiedosta pelin aikana!''' Tämä tarkoittaa, ettet voi esim. kertoa kätesi sisällöstä, mitä aiot tehdä tai mitä muiden kannattaisi tehdä. Korttien määristä ja muista pelialueen kohteista voi puhua. == Pelin kierroksen vaiheet == Pelaajat pelaavat yksitellen vuoronsa. Vuorossa on 4 vaihetta: # Pelaa kortti/kortteja tai peräänny # Aktivoi pelatut maakyvyt # Tee vahinkoa viholliselle ja tarkista # Ota vastaan vahinkoa viholliselta Vaiheitten jälkeen alkaa seuraavan pelaajan vuoro. === Pelaa kortteja tai peräänny === Pelaaja voi pelata kädestään kortteja seuraavanlaisesti: * Yksittäisen kortin * Yksittäisen kortin eläinkumppani-kortin kanssa (ässät) * Sarjan kortteja (kombot) ==== Eläinkumppanit ==== Eläinkumppanikortit (eli ässät) voidaan pelata parina toisen kortin kanssa. Ässät lasketaan 1 arvoisiksi. ==== Kombot ==== Samanarvoisia kortteja voi pelata yhdessä, mutta niiden summa ei saa ylittää 10. ==== Perääntyminen ==== Pelaaja voi myös '''perääntyä''', jolloin hän ei pelaa kädestään kortteja, mutta ottaa silti vahinkoa viholliselta. '''Huom!''' Kierroksen aikana vähintään yhden pelaajan on pelattava kortteja, joten jos muut pelaajat ovat perääntyneet omalla vuorollaan, viimeisen pelaajan on '''pakko''' pelata korttejaan. === Maakykyjen aktivointi === Neljällä maalla on omat kykynsä, jotka auttavat pelaajia: * Pata – suojaa pelaajia vähentämällä pelatun kokonaisarvon verran vihollisen hyökkäystä * Risti – tuplaa pelaajan hyökkäysvoimaa * Hertta – sekoittaa poistopinon, ottaa kokonaisarvon verran kortteja ja siirtää ne tavernapakan alle * Ruutu – nostaa kokonaisarvon verran kortteja tavernapakasta; jokainen pelaaja nostaa vuorotellen yhden kortin pakasta, aloittaen kyseisen vuoron pelaajasta Jos hertta ja ruutu pelataan samaan aikaan, hertta suoritetaan ensimmäisenä. Tietyn maan kykyä aktivoidaan vain kerran. ==== Vihollisten maasuoja ==== Jos kortilla on sama maa kuin vihollisen kortilla, vihollisella on suoja sitä vastaan. Tämä tarkoittaa, ettei kyseinen kyky aktivoidu tässä vaiheessa. Pelaaja voi poistaa suojan '''moninpelissä''' pelaamalla hovinarrin. Patojen kohdalla ennestään pelatut patakortit aktivoituvat. === Tee vahinkoa viholliselle === Pelaaja tekee pelattujen korttien verran vahinkoa nykyiselle viholliselle. Jos pelaajien pelaamien vahingon määrä ylittää vihollisen terveyden, hänet kukistetaan ja asetetaan poistopinoon vihollisen aikana pelattujen korttien mukana. Linnapakasta käännetään uusi vihollinen, ja pelaajan vuoro alkaa alusta. '''Huom!''' Jos pelaajat tekevät tasan saman verran vahinkoa kuin vihollisen terveys, kukistettu vihollinen asetetaan poistopinon sijaan tavernapakan päällimmäiseksi kortiksi. === Vihollinen hyökkää === Vihollinen hyökkää vuoroaan pelaavaa pelaajaa vastaan. Hänen on poistettava kädestään vähintään vihollisen hyökkäysvoiman verran arvoisia kortteja poistopinoon. '''Jos pelaaja ei pysty poistamaan tarpeeksi kortteja kädestään, peli päättyy pelaajien häviöön!''' Hyökkäyksen jälkeen vuoro siirtyy seuraavalle pelaajalle. == Pelin päättyminen == Pelaajat voittavat, jos viimeinen kuningas kukistetaan. Jos pelaaja ei pysty poistamaan tarpeeksi kortteja hyökkäyksen takia, pelaajat häviävät. Pelaajat voivat myös hävitä, jos pelaaja ei pysty vuorollaan pelaamaan korttia tai perääntymään. 0c00ec14c0f336c6108b849fd3b0ad6514df0428 1237 1236 2022-07-20T10:20:54Z Rexroom 5688 /* Alkuasettelu */ wikitext text/x-wiki Yhteistyöpeli 1–4 pelaajalle, jossa pelaajat yrittävät kukistaa voimakkaita vihollisia peruskorttipakalla. == Alkuasettelu == Kuninkaallisista rakennetaan linnapakka, josta nostetaan vihollisia. Ensiksi nostetaan pelin aikana jätkiä, sitten kuningattaria ja lopulta kuninkaita. Loput kortit ja mahdolliset hovinarri-kortit sekoitetaan tavernapakaksi, josta pelaajat nostavat alussa kortteja käsikoon verran. {| class="wikitable" style="margin:auto; text-align:center" ! Pelaajia !! Hovinarreja !! Käsikoko |- | 1 || 0 || 8 |- | 2 || 0 || 7 |- | 3 || 1 || 6 |- | 4 || 2 || 5 |} == Viholliset == {| class="wikitable" style="margin:auto; text-align:center" ! Viholliset !! Terveys !! Hyökkäys |- | Jätkät || 20 || 10 |- | Kuningattaret || 30 || 15 |- | Kuninkaat || 40 || 20 |} Kukistetut viholliset pelaajan kädessä toimivat kuin normaalit kortit, mutta niiden arvot ovat listauksen hyökkäysten arvoisia. == Ei suoraa viestintää! == Koska kyseessä on yhteistyöpeli, '''pelaajat eivät saa puhua keskenään mistään muusta kuin julkisesta tiedosta pelin aikana!''' Tämä tarkoittaa, ettet voi esim. kertoa kätesi sisällöstä, mitä aiot tehdä tai mitä muiden kannattaisi tehdä. Korttien määristä ja muista pelialueen kohteista voi puhua. == Pelin kierroksen vaiheet == Pelaajat pelaavat yksitellen vuoronsa. Vuorossa on 4 vaihetta: # Pelaa kortti/kortteja tai peräänny # Aktivoi pelatut maakyvyt # Tee vahinkoa viholliselle ja tarkista # Ota vastaan vahinkoa viholliselta Vaiheitten jälkeen alkaa seuraavan pelaajan vuoro. === Pelaa kortteja tai peräänny === Pelaaja voi pelata kädestään kortteja seuraavanlaisesti: * Yksittäisen kortin * Yksittäisen kortin eläinkumppani-kortin kanssa (ässät) * Sarjan kortteja (kombot) ==== Eläinkumppanit ==== Eläinkumppanikortit (eli ässät) voidaan pelata parina toisen kortin kanssa. Ässät lasketaan 1 arvoisiksi. ==== Kombot ==== Samanarvoisia kortteja voi pelata yhdessä, mutta niiden summa ei saa ylittää 10. ==== Perääntyminen ==== Pelaaja voi myös '''perääntyä''', jolloin hän ei pelaa kädestään kortteja, mutta ottaa silti vahinkoa viholliselta. '''Huom!''' Kierroksen aikana vähintään yhden pelaajan on pelattava kortteja, joten jos muut pelaajat ovat perääntyneet omalla vuorollaan, viimeisen pelaajan on '''pakko''' pelata korttejaan. === Maakykyjen aktivointi === Neljällä maalla on omat kykynsä, jotka auttavat pelaajia: * Pata – suojaa pelaajia vähentämällä pelatun kokonaisarvon verran vihollisen hyökkäystä * Risti – tuplaa pelaajan hyökkäysvoimaa * Hertta – sekoittaa poistopinon, ottaa kokonaisarvon verran kortteja ja siirtää ne tavernapakan alle * Ruutu – nostaa kokonaisarvon verran kortteja tavernapakasta; jokainen pelaaja nostaa vuorotellen yhden kortin pakasta, aloittaen kyseisen vuoron pelaajasta Jos hertta ja ruutu pelataan samaan aikaan, hertta suoritetaan ensimmäisenä. Tietyn maan kykyä aktivoidaan vain kerran. ==== Vihollisten maasuoja ==== Jos kortilla on sama maa kuin vihollisen kortilla, vihollisella on suoja sitä vastaan. Tämä tarkoittaa, ettei kyseinen kyky aktivoidu tässä vaiheessa. Pelaaja voi poistaa suojan '''moninpelissä''' pelaamalla hovinarrin. Patojen kohdalla ennestään pelatut patakortit aktivoituvat. === Tee vahinkoa viholliselle === Pelaaja tekee pelattujen korttien verran vahinkoa nykyiselle viholliselle. Jos pelaajien pelaamien vahingon määrä ylittää vihollisen terveyden, hänet kukistetaan ja asetetaan poistopinoon vihollisen aikana pelattujen korttien mukana. Linnapakasta käännetään uusi vihollinen, ja pelaajan vuoro alkaa alusta. '''Huom!''' Jos pelaajat tekevät tasan saman verran vahinkoa kuin vihollisen terveys, kukistettu vihollinen asetetaan poistopinon sijaan tavernapakan päällimmäiseksi kortiksi. === Vihollinen hyökkää === Vihollinen hyökkää vuoroaan pelaavaa pelaajaa vastaan. Hänen on poistettava kädestään vähintään vihollisen hyökkäysvoiman verran arvoisia kortteja poistopinoon. '''Jos pelaaja ei pysty poistamaan tarpeeksi kortteja kädestään, peli päättyy pelaajien häviöön!''' Hyökkäyksen jälkeen vuoro siirtyy seuraavalle pelaajalle. == Pelin päättyminen == Pelaajat voittavat, jos viimeinen kuningas kukistetaan. Jos pelaaja ei pysty poistamaan tarpeeksi kortteja hyökkäyksen takia, pelaajat häviävät. Pelaajat voivat myös hävitä, jos pelaaja ei pysty vuorollaan pelaamaan korttia tai perääntymään. dbbf1b6ffe6c2054a06ce998a0db2e6e3b2f20b5 Gamehelpmartiandice 0 177 1238 1167 2022-08-15T07:32:03Z Rexroom 5688 Sääntöjen selkeyttämistä wikitext text/x-wiki == Pelin päämäärä == Peliä pelataan kunnes joku saa yli 25 pistettä. Eniten pisteitä saanut voittaa. == Pelin kulku == Peli etenee myötäpäivää kierroksittain. === Pelaajan vuoro === # Pelaaja heittää kaikki vapaana olevat nopat. Jos vapaita noppia ei ole, siirry pisteytykseen. # Siirrä kaikki heitetyt tankit tankkipinoon. # Jäljelle jääneistä nopista valitse yksi ryhmä nopan silmistä (kanat, lehmät, ihmiset tai kuolemansäteet). #* Jokaista ryhmää voidaan valita vain kerran, et voi kasvattaa maan asukas -pinoja vuorosi aikana. Poikkeuksena tähän ovat kuolemansäteet, joita voi valita aina. #* Jos et voi siirtää yhtään noppaa sivulle, siirry pisteytykseen. # Voit joko siirtyä pisteytykseen ja lopettaa kierroksesi tai aloittaa vuorosi alusta heittämällä jäljellä olevia noppia. === Vuoron pisteytys === Jos tankkeja on enemmän kuin kuolemansäteitä, et saa tältä kierrokselta pisteitä. Muutoin saat pisteen jokaisesta kaapatusta nopasta. Saat vielä 3 lisäpistettä jos olet kaapannut kaikki 3 maan asukas -luokkaa tällä vuorolla. Pisteytyksen lopussa kaikki nopat annetaan seuraavalle pelaajalle. === Pelin lopetus === Kun joku saa yli 25 pistettä, kierros pelataan loppuun. Tasapeliin päätyneet pelaajat heittävät 6 noppaa, eniten kuolemansäteitä saanut voittaa pelin. 0c69f69c21939d6bfb929a39a610e35798321dc4 Gamehelpwingspan 0 201 1239 2022-09-26T16:56:59Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Voita peli luomalla vakuuttavin lintujen suojelualue. == Alkuvalmistelut == Jokainen pelaaja saa alussa 5 lintukorttia, 2 bonuskorttia ja 5 erityyppistä ruokamerkkiä. '''Pelaajat valitsevat enintään 5 lintukorttia, ja jokaisesta käteen jääneestä lintukortista pitää maksaa 1 ruokamerkki.''' Sitten valitset kahdesta bonuskortista yhden. Aloituspelaaja arvotaan. === Tavoitelauta === Pelissä on 4 kierroksen aikana eri päämääriä, josta pelaajat saavat pisteitä kierroksen lopussa. Tavoitelaudalle arvotut tavoitelaatat kertovat, mikä on pelatun kierroksen päämäärä. Vihreä tavoitelauta on suositeltu puoli, jossa pelataan sijoista ja on tarkoitettu kilpailullisille peleille. Sinisessä puolessa saat pisteen jokaisesta päämäärän pyytämistä esineistä, ja suositellaan ensimmäisille tai rennommille pelikerroille. == Pelin rakenne == Peli kestää 4 kierrosta. Jokainen kierros pitää sisällään toimintokuutioiden verran olevia vuoroja. === Vuoron kulku === Voit tehdä vuorosi aikana yhden neljästä toiminnosta: '''1. Pelata linnun kädestäsi''' '''2. Hankkia ruokaa ja aktivoida metsän lintujen ominaisuuksia''' '''3. Munia ja aktivoida avomaan lintujen ominaisuuksia''' '''4. Nostaa lintukortteja ja aktivoida kosteikon lintujen ominaisuuksia''' === Pelaa lintu === Pelaa lintu kädestäsi sopivaan elinympäristön vapaaseen sarakkeeseen (alkaen vasemmasta) ja maksa vapaan sarakkeen munakustannus ja lintukortin ruokakustannus. Jos kortissa on "Kun pelaat" -ominaisuus, se aktivoituu kun olet asettanut kortin laudallesi. '''Huom!''' Voit vaihtaa kaksi ruokamerkkiä yhteen puuttuvaan ruokamerkkiin. === Hanki ruokaa === Metsäelinympäristön kautta voit hankkia ruokaa ruokinta-automaatista. Katso vasemmanpuoleisen vapaaseen ruutuun ja poimi sen määrän mukaisesti noppia ruokinta-automaatista. Nopista näkyy, mitä ruokamerkkejä voit nostaa itsellesi. Jos ruudussa on myös "kortista nopaksi" -muuntobonus, voit halutessasi heittää kortin pois ja poimia yhden ylimääräisen nopan. '''Huom.''' Jos ruokinta-automaatissa on enää yksi jäljelläoleva noppa, voit halutessasi heittää kaikkia noppia uudestaan. Tämän jälkeen aktivoit metsän lintujen ominaisuuksia, alkaen oikealta. === Muni munia === Ota vasemmanpuoleisen vapaan ruudun verran munia ja sijoita ne vapaasti lintukortteihisi. Huomaa, että linnuilla on munamaksimit, joita ei voi ylittää. Jos ruudussa on myös "ruoasta munaksi" -muuntobonus, voit halutessasi maksaa 1 ruokamerkin ja ottaa vielä yhden munan. Tämän jälkeen aktivoit avomaan lintujen ominaisuuksia, alkaen oikealta. === Nosta lintukortteja === Nosta vasemmanpuoleisen vapaan ruudun verran kortteja. Ne voit nostaa joko lintukorttitelineestä (kolme kuvapuoli ylöspäin näkyviä kortteja) tai lintukorttipakan päältä. Jos ruudussa on myös "munasta kortiksi" -muuntobonus, voit halutessasi maksaa 1 munan ja nostaa vielä yhden kortin. Tämän jälkeen aktivoit kosteikon lintujen ominaisuuksia, alkaen oikealta. == Lintukorttien aktivoiminen == Ruskeat ("kun aktivoit") lintukortit aktivoituvat, kun käytät sen nykyisen elinympäristönsä toimintoa. Vaaleanpunaiset ("kerran vuorojesi välissä") lintukortit aktivoituvat, kun muut pelaajat tekevät jotain, joka vastaa ominaisuuden tekstiä (esim. pelaavat metsälinnun lautaansa). Valkoiset ("kun pelaat") lintukortit aktivoituvat, kun pelaat kyseiset lintukortit laudallesi. == Kierroksen loppu == Kierros päättyy, kun pelaajat ovat käyttäneet kaikki toimintokuutionsa. Sitten pelaajien yksi toimintokuutioista asetetaan tavoitelaudan kierrosta vastaavalle riville kierroksen päämäärän mukaiseen paikkaan, ja uusi kierros alkaa. '''Huomaa, että pelaajilla on uuden kierroksen alkaessa yksi toimintokuutio vähemmän. Kun pelaajat aloittavat 4. kierroksen, heidän 8 toimintokuutiota ovat harventuneet 5 kuutioon. Aloituspelaajan merkki siirtyy seuraavalle pelaajalle kierroksen lopussa, joka aloittaa seuraavan kierroksen. == Pelin päättyminen == Peli päättyy, kun neljäs ja viimeinen kierros pelataan loppuun. Loppupisteytys suoritetaan: * Pelattujen lintukorttien pisteiden summa * Bonuskorteista saadut pisteet * Tavoitelaudan pisteet * Munien määrä * Säilötyt ruoat * Piilotetut kortit Eniten pisteitä kerännyt pelaaja voittaa. Tasapelit ratkaisee jäljelle jääneet ruokamerkit. 11245471f46ed6489ba03b26144d184448ac9a09 1240 1239 2022-09-26T17:13:12Z Rexroom 5688 Bonuskorteista wikitext text/x-wiki Voita peli luomalla vakuuttavin lintujen suojelualue. == Alkuvalmistelut == Jokainen pelaaja saa alussa 5 lintukorttia, 2 bonuskorttia ja 5 erityyppistä ruokamerkkiä. '''Pelaajat valitsevat enintään 5 lintukorttia, ja jokaisesta käteen jääneestä lintukortista pitää maksaa 1 ruokamerkki.''' Sitten valitset kahdesta bonuskortista yhden. Aloituspelaaja arvotaan. === Tavoitelauta === Pelissä on 4 kierroksen aikana eri päämääriä, josta pelaajat saavat pisteitä kierroksen lopussa. Tavoitelaudalle arvotut tavoitelaatat kertovat, mikä on pelatun kierroksen päämäärä. Vihreä tavoitelauta on suositeltu puoli, jossa pelataan sijoista ja on tarkoitettu kilpailullisille peleille. Sinisessä puolessa saat pisteen jokaisesta päämäärän pyytämistä esineistä, ja suositellaan ensimmäisille tai rennommille pelikerroille. === Bonuskorteista === Koska jotkut bonuskorteista poikkeavat eri kielipainosten välillä, on syytä harkita kyseisten kieliriippuvaisten bonuskorttien jättämistä pois pelistä, etenkin jos pelaatte kansainvälisiä pelaajia vastaan. Tämän pelin mukaelma huomioi vain englanninkielisen laitoksen bonuskorttien ehtoja, jotka viittaavat lintujen englanninkielisiin nimiin. '''Kyseiset bonuskortit siten viittaavat englanninkielisiin lintujen nimiin, eivätkä suomenkielisiin.''' == Pelin rakenne == Peli kestää 4 kierrosta. Jokainen kierros pitää sisällään toimintokuutioiden verran olevia vuoroja. === Vuoron kulku === Voit tehdä vuorosi aikana yhden neljästä toiminnosta: '''1. Pelata linnun kädestäsi''' '''2. Hankkia ruokaa ja aktivoida metsän lintujen ominaisuuksia''' '''3. Munia ja aktivoida avomaan lintujen ominaisuuksia''' '''4. Nostaa lintukortteja ja aktivoida kosteikon lintujen ominaisuuksia''' === Pelaa lintu === Pelaa lintu kädestäsi sopivaan elinympäristön vapaaseen sarakkeeseen (alkaen vasemmasta) ja maksa vapaan sarakkeen munakustannus ja lintukortin ruokakustannus. Jos kortissa on "Kun pelaat" -ominaisuus, se aktivoituu kun olet asettanut kortin laudallesi. '''Huom!''' Voit vaihtaa kaksi ruokamerkkiä yhteen puuttuvaan ruokamerkkiin. === Hanki ruokaa === Metsäelinympäristön kautta voit hankkia ruokaa ruokinta-automaatista. Katso vasemmanpuoleisen vapaaseen ruutuun ja poimi sen määrän mukaisesti noppia ruokinta-automaatista. Nopista näkyy, mitä ruokamerkkejä voit nostaa itsellesi. Jos ruudussa on myös "kortista nopaksi" -muuntobonus, voit halutessasi heittää kortin pois ja poimia yhden ylimääräisen nopan. '''Huom.''' Jos ruokinta-automaatissa on enää yksi jäljelläoleva noppa, voit halutessasi heittää kaikkia noppia uudestaan. Tämän jälkeen aktivoit metsän lintujen ominaisuuksia, alkaen oikealta. === Muni munia === Ota vasemmanpuoleisen vapaan ruudun verran munia ja sijoita ne vapaasti lintukortteihisi. Huomaa, että linnuilla on munamaksimit, joita ei voi ylittää. Jos ruudussa on myös "ruoasta munaksi" -muuntobonus, voit halutessasi maksaa 1 ruokamerkin ja ottaa vielä yhden munan. Tämän jälkeen aktivoit avomaan lintujen ominaisuuksia, alkaen oikealta. === Nosta lintukortteja === Nosta vasemmanpuoleisen vapaan ruudun verran kortteja. Ne voit nostaa joko lintukorttitelineestä (kolme kuvapuoli ylöspäin näkyviä kortteja) tai lintukorttipakan päältä. Jos ruudussa on myös "munasta kortiksi" -muuntobonus, voit halutessasi maksaa 1 munan ja nostaa vielä yhden kortin. Tämän jälkeen aktivoit kosteikon lintujen ominaisuuksia, alkaen oikealta. == Lintukorttien aktivoiminen == Ruskeat ("kun aktivoit") lintukortit aktivoituvat, kun käytät sen nykyisen elinympäristönsä toimintoa. Vaaleanpunaiset ("kerran vuorojesi välissä") lintukortit aktivoituvat, kun muut pelaajat tekevät jotain, joka vastaa ominaisuuden tekstiä (esim. pelaavat metsälinnun lautaansa). Valkoiset ("kun pelaat") lintukortit aktivoituvat, kun pelaat kyseiset lintukortit laudallesi. == Kierroksen loppu == Kierros päättyy, kun pelaajat ovat käyttäneet kaikki toimintokuutionsa. Sitten pelaajien yksi toimintokuutioista asetetaan tavoitelaudan kierrosta vastaavalle riville kierroksen päämäärän mukaiseen paikkaan, ja uusi kierros alkaa. '''Huomaa, että pelaajilla on uuden kierroksen alkaessa yksi toimintokuutio vähemmän. Kun pelaajat aloittavat 4. kierroksen, heidän 8 toimintokuutiota ovat harventuneet 5 kuutioon. Aloituspelaajan merkki siirtyy seuraavalle pelaajalle kierroksen lopussa, joka aloittaa seuraavan kierroksen. == Pelin päättyminen == Peli päättyy, kun neljäs ja viimeinen kierros pelataan loppuun. Loppupisteytys suoritetaan: * Pelattujen lintukorttien pisteiden summa * Bonuskorteista saadut pisteet * Tavoitelaudan pisteet * Munien määrä * Säilötyt ruoat * Piilotetut kortit Eniten pisteitä kerännyt pelaaja voittaa. Tasapelit ratkaisee jäljelle jääneet ruokamerkit. 3831b648eb1fabba4407b753ef5bd413cc876183 Gamehelpcarcassonne 0 178 1241 1173 2022-10-15T13:41:51Z Rexroom 5688 Lisäosat wikitext text/x-wiki == Päämäärä == Pelaajat asettelevat vuorottain pelialueelle laattoja, joka pelin edetessä muodostuu kaupunkien, luostareiden ja teiden verkostoksi. Pelaajat saavat pisteitä sen mukaan, kun saavat seuraajiensa pitämiä alueita valmiiksi. Pelaajien asetettua viimeisetkin laatat, peli loppuu ja lasketaan loputkin pisteistä. Pelaaja, joka on kerännyt eniten pisteitä lopussa, on voittaja. == Pelin kulku == Vuoronsa alussa pelaaja asettaa pinosta nostamansa laatan ja asettaa sen pelilaudalla siten, että se sopii kokonaisuuteen. Pelaaja voi sen jälkeen asettaa seuraajansa laattaan eri tarkoitusta varten, kuten maantierosvoksi teille, ritariksi kaupunkiin, munkiksi luostariin tai talonpojaksi pelloille. ''Huomaa, että et voi lisätä esim. samalle tielle toista seuraajaa.'' Alueen tulee olla vapaa muista seuraajista. Näiden vaiheiden jälkeen alkaa alueen mahdollinen pisteytys jos jokin alue saatiin valmiiksi. Sitten vuoro siirtyy seuraavalle pelaajalle. === Pisteytys === Kun pelaajat saavat jonkun alueen valmiiksi, alkaa pisteytys. Tie valmistuu, kun tien kummatkin päät loppuvat jonnekin. Pelaaja saa niin paljon pisteitä kuin tien pituus on laattoina. Kaupunki valmistuu, kun sitä ympäröivä muuri saadaan valmiiksi. Pelaaja saa 2 p. * kaupunkilaatta + 2 p. * vaakunakilpi. Luostari valmistuu, kun 8 sitä ympäröivää laattaa on sijoitettu. Pelaaja saa pisteen jokaisesta ympärillä olevasta laatasta ja itse luostarista. Jokaisesta valmistuneesta alueesta palautetaan seuraajat pelaajien varastoon uutta käyttöä varten. === Useita seuraajia samassa alueessa === Pelaajat eivät voi lisätä seuraajiaan alueelle jossa on jo yksi, mutta he voivat yhdistää eri alueet yhdeksi. Siten pisteytyksen yhteydessä jos jollakin pelaajalla on enemmän seuraajia vastavalmistuneella alueella, vain hän saa niistä pisteet. Tasatilanteissa alueen pisteet annetaan kaikille tasapeliin päätyneille. === Loppupisteytys === Loppupisteytys alkaa siinä vaiheessa, kun pelilaatat loppuvat kesken. Siinä vaiheessa keskeneräiset tiet, kaupungit ja luostarit pisteytetään. Pisteytys on sama kaupunkeja lukuunottamatta, jossa kaupunkilaatat ja vaakunakilvet ovat vain 1 pisteen arvoisia. Viimeiseksi lasketaan talonpoikien pisteet. Jokainen talonpojan asuttamaa peltoa koskettava valmis kaupunki tuottaa 3 pistettä. Eniten pisteitä kerännyt pelaaja on voittaja. == Laajennukset == BGA:n adaptaatiossa on valittavana neljä lisäosaa, jotka voidaan ottaa mukaan ennen pelin alkua pöydän peliasetuksista. === Joki === Peli alkaa jokilaatoilla, jotka pelaajat sijoittavat vuorotellen. Joki ei voi myöskään kääntyä kaksi kertaa peräkkäin samaan suuntaan. Pelaajat voivat myös sääntöjen mukaisesti laittaa seuraajia laatoille tavalliseen tapaan. === Kirkot ja kievarit === Valmistuneet tiet, jonka varrella on kievari(t), antavat 2 pistettä laattaa kohden 1 pisteen sijaan. Pelin lopussa keskeneräiset tiet, joissa on kievari(t), eivät anna pisteitä. Valmistuneet kaupungit, joissa on katedraali, antavat 3 pistettä laattaa kohden 2 pisteen sijaan. Pelin lopussa keskeneräiset kaupungit, joissa on katedraali, eivät anna pisteitä. Lisäosan mukana tulevat suuret seuraajat, joita kaikilla pelaajilla on yksi. Sitä pelataan kuin tavallista seuraajaa, mutta ne ovat arvoltaan 2 seuraajaa pisteytyksissä. === Kirjurit ja kauppiaat === Uudet kaupunkilaatat sisältävät kauppatavaroita, joita on kolmea lajia: viiniä, viljaa ja kanagasta. Jos pelaaja saa valmiiksi kaupungin, hän saa valmiissa kaupungissa olevat kauppatavarat. Huomaa, ettei kauppatavaroiden saamiseen tarvita enemmistöä! Pelin lopussa kauppatavarat pisteytetään. Jokaista kauppatavaralajin enemmistöstä saa 10 pistettä. Tasapeleissä tasoissa olevat pelaajat saavat 10 pistettä. Kirjurit toimivat erikoisnappuloina, joita voi pelata seuraajan sijasta teihin tai kaupunkeihin, jossa on jo oma seuraajasi. Kirjuri palaa varastoosi, jos sillä ei ole jossain vaiheessa seuraajia seuranaan. Sen jälkeen, kun jatkat tietä tai kaupunkia, jossa on kirjurisi, voit nostaa toisen laatan, jota pelata saman tien. Huomaa, että tämä lisätoiminto toimii vain kerran vuorosi aikana. Possun voi pelata pellolle, jossa on oma talonpoika. Se pysyy pellolla pelin loppuun saakka. Loppupisteytyksessä possut antavat enemmistönä pellon ympärillä olevista kaupungeista 4 pistettä 3 pisteen sijaan. === Lohikäärme ja linnanneito === 9cdb2492c0d2d867a5dfba4a0b53e3aaa816d995 Gamehelplama 0 202 1242 2022-10-30T22:44:24Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Pelaajat yrittävät välttää keräämästä miinuspisteitä pelaamalla korttejaan pois ennen kierroksen loppumista. == Pelin kulku == Kierros aloitetaan sekoittamalla pakkaa ja jakamalla pelaajille 6 korttia. Aloituspelaaja on edellisen kierroksen lopettaja. Pelaaja voi vuorollaan tehdä yhden kolmesta toiminnosta: * Pelata kortin, joka on samanarvoinen tai yhden isompi kuin poistopinon päällimmäinen kortti * Nostaa pakasta yhden kortin, kunhan pakassa on vielä kortteja ja et ole viimeinen, joka ei ole vielä passannut * Passata, jonka jälkeen hänelle ei enää tule uusia vuoroja kyseisellä kierroksella '''Huom!''' Laama-kortteja voi pelata vain, jos poistopinon päällimmäinen on kortti on 6 tai laama. Laaman päälle voi pelata myös 1-kortin. === Kierroksen päättyminen ja pisteytys === Kierros päättyy, jos joko yksi pelaajista on pelannut kaikki kätensä kortit tai kaikki ovat passanneet. Jos pelaaja sai kätensä tyhjäksi korteista, hän voi palauttaa yhden pistemerkeistä varastoon. (BGA:ssa peli palauttaa aina suurimman pistemerkin takaisin.) Sitten kaikkien pelaajien kädet pisteytetään. Jokainen numeroarvo pisteytetään vain kerran, eli jos jollakin on kolme 6-korttia, hän saa vain yhdestä kortista miinuspisteitä, eli tässä tapauksessa vain -6 pistettä. '''Laamakortit ovat 10 miinuspisteen arvoisia.''' Esimerkiksi pelaajalla A on kierroksen lopussa seuraavanlainen käsi: 3, 3, 4, 5, Laama. Niistä tulee miinuspisteitä 3+4+5+10 = -22. == Pelin päättyminen == Peli päättyy, kun joku pelaajista on kerännyt vähintään 40 miinuspistettä. Pelaaja, jolla on vähiten miinuspisteitä, voittaa pelin. Tasapelissä voitto jaetaan. 19901c8d2398597bb2fd92bd544c85daf0005d1b 1243 1242 2022-10-30T22:45:01Z Rexroom 5688 /* Pelin kulku */ wikitext text/x-wiki Pelaajat yrittävät välttää keräämästä miinuspisteitä pelaamalla korttejaan pois ennen kierroksen loppumista. == Pelin kulku == Kierros aloitetaan sekoittamalla pakkaa ja jakamalla pelaajille 6 korttia. Aloituspelaaja on edellisen kierroksen lopettaja. Pelaaja voi vuorollaan tehdä yhden kolmesta toiminnosta: * Pelata kortin, joka on samanarvoinen tai yhden isompi kuin poistopinon päällimmäinen kortti * Nostaa pakasta yhden kortin, kunhan pakassa on vielä kortteja ja et ole viimeinen, joka ei ole vielä passannut * Passata, jonka jälkeen hänelle ei enää tule uusia vuoroja kyseisellä kierroksella '''Huom!''' Laama-kortteja voi pelata vain, jos poistopinon päällimmäinen kortti on 6 tai laama. Laaman päälle voi pelata myös 1-kortin. === Kierroksen päättyminen ja pisteytys === Kierros päättyy, jos joko yksi pelaajista on pelannut kaikki kätensä kortit tai kaikki ovat passanneet. Jos pelaaja sai kätensä tyhjäksi korteista, hän voi palauttaa yhden pistemerkeistä varastoon. (BGA:ssa peli palauttaa aina suurimman pistemerkin takaisin.) Sitten kaikkien pelaajien kädet pisteytetään. Jokainen numeroarvo pisteytetään vain kerran, eli jos jollakin on kolme 6-korttia, hän saa vain yhdestä kortista miinuspisteitä, eli tässä tapauksessa vain -6 pistettä. '''Laamakortit ovat 10 miinuspisteen arvoisia.''' Esimerkiksi pelaajalla A on kierroksen lopussa seuraavanlainen käsi: 3, 3, 4, 5, Laama. Niistä tulee miinuspisteitä 3+4+5+10 = -22. == Pelin päättyminen == Peli päättyy, kun joku pelaajista on kerännyt vähintään 40 miinuspistettä. Pelaaja, jolla on vähiten miinuspisteitä, voittaa pelin. Tasapelissä voitto jaetaan. 616f182de48959e635d9ce652901b96f9e3b75d6 Gamehelpbreakthecode 0 203 1244 2022-11-21T18:43:25Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Koodinratkontaa 2–4 pelaajalle. Yritä ratkaista 4 tai 5 numeroisen koodin. == Numerolaatoista == Pelaajille jaettavat numerolaatat asetetaan nousevaan numerojärjestykseen. Tapauksissa, jossa on kaksi samaa mutta eriväristä numeroa, musta asetetaan ennen valkoista. == Pelin kulku == Peli eroaa merkittävästi riippuen pelaajien määristä, joten lue vastaava kappale. === Kaksinpeli === Pelaajille jaetaan 5 numerolaattaa. Pelaajat yrittävät ratkaista vastustajansa koodin. Pelaajat voivat vuorollaan tehdä yhden kahdesta toiminnoistaan: '''Ota kysymyskortti''' ja lue se. Kilpailija vastaa rehellisesti kysymykseen. '''Ratkaise koodi''' ja ilmoita yksitellen jokaisen 5 laatan väri ja numero. Peli jatkuu, kunnes joku arvaa koodin tai putoaa. Jos joku arvaa koodin oikein, toinen pelaaja voi vielä pelata tasapelistä arvaamalla toisen pelaajan koodin. === 3–4 pelaajan peli === 3-pelaajan pelissä pelaajille jaetaan 5 numerolaattaa, 4-pelaajan pelissä 4 numerolaattaa. Jäljelle jääneet laatat asetetaan keskelle kuvapuoli alaspäin. Pelaajat yrittävät ratkaista keskellä olevien laattojen koodin selvittämällä muiden pelaajien laatat. Pelaajat voivat vuorollaan tehdä yhden kahdesta toiminnoistaan: '''Ota kysymyskortti''' ja lue se. Kilpailijat vastaavat rehellisesti kysymykseen. Nelinpelissä kysymyksen esittäjä myös vastaa! '''Ratkaise koodi''' ja ilmoita yksitellen jokaisen 5 laatan väri ja numero. Peli jatkuu, kunnes joku arvaa koodin tai kaikki putoavat pelistä. Jokaisella on kuitenkin saman verran vuoroja pelattavana, joten kierros päättyy viimeistään ennen aloituspelaajaa pelaavalle pelaajaan jos joku ratkaisi kierroksessa koodin. == Lehtiö == Pelaajilla on oma lehtiö, johon he voivat kirjata merkintöjään. Jos asetettu, peli merkitsee varmat tiedot ylös lehtiöön. Voit myös merkitä sudoku-tyyliin yksittäisiä numeroita lehtiöösi. Voit myös merkitä mahdolliset väritkin. Käyttämällä perusteellisesti lehtiötä, helpotat huomattavasti tutkimustyötäsi! Lehtiöllä on kolme aluetta: ylävasemmalla on mahdolliset numerot väreineen, joita voit korostaa tai yliviivata. Oikealla yläkulmalla voit merkitä mahdollisen ratkaistavan koodin. (Tämä ei ole kaksinpeleissä käytössä.) Keskellä on muiden pelaajien laatat. Oikeaan reunaan, jos asetettu, tulee kysymysten vastaukset. b0cc6d38327391d3d35ea14dbb1fa24b629bcf08 Gamehelphaiclue 0 204 1245 2022-12-12T20:30:33Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Sananarvauspeli 2–12 pelaajalle. == Pelin kulku == Kierroksen alussa paljastetaan pelialueeseen neljä sanaa. Noista neljästä sanasta yksi määrätään salaisesti pelaajan sanaksi. Pelaajien tehtävänä on rakentaa pelin alussa jaetuista 15 sanasta vinkki, joka viittaa pelialueen pelaajan sanaan. Kun kaikki ovat rakentaneet vinkkinsä, alkaa arvaaminen. Pelaajat pyrkivät vinkistä päättelemään pelaajan sanan. Kaikkien pelaajien vinkit käydään vuorotellen läpi. Vinkin rakentanut pelaaja ei liity arvailuihin. Tämän jälkeen alkaa pisteytys. Pelaajat saavat 1 pisteen jokaisesta oikeasta arvauksesta, ja vinkin rakentanut pelaaja saa myös 1 pisteen jokaisesta arvatusta sanasta. '''Huom!''' Pelissä on suomenkieliset sanat. Jos haluat käyttää niitä, valitse pöydän asetuksista ''Game Language'' -pudotusvalikosta "Suomi". === Pelin päättyminen === Peli päättyy, kun määrätty määrä kierroksia on suoritettu. Eri pelaajmäärillä on eri määrä kierroksia: * 2 pelaajaa / 4 kierrosta * 3–5 pelaajaa / 6 kierrosta * 6–8 pelaajaa / 4 kierrosta * 9–12 pelaajaa / 2 kierrosta Pelin lopussa eniten pisteitä kerännyt pelaaja voittaa pelin. Tasapelejä ei ratkaista. === Kaksinpeli === Kaksinpeli toimii yhteistyöpelinä, jossa yritätte arvata toistenne sanoja. f50ccbbca729d0f053bac93da3c5ba5650d4f89e Gamehelpbeyondthesun 0 205 1246 2022-12-29T19:29:15Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Avaruussivilisaatiopeli 2–4 pelaajalle, jossa tehdään avaruustutkimusta ja käydään läpi "teknologiapuuta". == Ikonit == [SHIP]/[SUPPLY]/[POPULATION]: Pelaajakuutioita. Sinun [SUPPLY] odottavat pelaajalaudallasi ennen muuttamista [SHIP] tai [POPULATION]. [SUPPLY][ARROW][POPULATION]: Muunna yksi korikuutio populaatiokuutioksi. [ORE]: Malmia. Näet nykyisen malmivarantosi pelaajapaneelistasi. [LOSE_POPULATION]: Menetä populaatiokuutio (maksuna jostain toiminnosta). [I]/[II]/[III]/[IIII]: Tason 1/2/3/4-teknologia. [RESEARCH][TECHX]: Tutki teknologia. Vaatii [POPULATION]. [VP]: Voittopisteitä. [COLONIZE]: Kolonisoi planeettakunta. Sinun on hallittava se ja pitää siellä vaaditun määrän alusvoimaa. == Teknologia-alat == Pelissä on 4 erinäistä teknologia-alaa, joilla on omat erityispiirteensä. Jotkut tutkittavat teknologiat ovat kahden alan hybridejä. * Kaupankäynti – kolonisointitoiminnot, liikkumiset * Sotatekniikka – alusrakennus, liikkumiset, parantelut * Talous – ruoantuotantoautomatisointi, väestönkasvu, malmintuotanto * Tiede – malmintuotantoautomatisointi, tutkimustoiminnot, malmibonukset == Pelin kulku == Pelaajan vuorossa on kolme vaihetta: 1. Toimintavaihe: aseta työläisesi vapaaseen toimintoheksaan ja maksa mahdolliset kulut. 2. Tuotantovaihe: voit joko a) [POPULATION] kasvattaa väestöä, b) [ORE] tuottaa malmia tai c) tehdä resurssivaihtoa. Väestönkasvussa jokaisesta sarakkeesta, jonka alla ei ole ruoantuotantolevy, muuttuu 1 korikuutio populaatioksi. (Huom. Jos sarakkeessa ei ole kuutioita, et saa sieltä populaatiota.) Malmintuotannossa jokainen paljas malmisymboli malmintuotantoradalla antaa sinulle yhden malmin. 3. Saavutusvaihe: tarkista, onko pelaaja saavuttanut jonkun saavutuksista ja valitsee sitten '''yhden''' vuoronsa aikana == Pelin päättyminen == Kun 2–3-pelaajan pelissä on otettu 3 saavutusta tai 4-pelaajan pelissä 4 saavutusta jonkun pelaajan vuoron lopussa, kierros pelataan normaalisti loppuun, jonka jälkeen pelataan vielä viimeinen kierros alkaen aloituspelaajasta. Tämän jälkeen alkaa loppupisteytys. Eniten voittopisteitä kerännyt pelaaja voittaa pelin. Tasapelit ratkaistaan ensin pelaajien tuotantotasojen summan mukaan, toisena kenellä on eniten populaatiokuutioita ja viimeisenä ratkaisijana malmimäärän mukaan. 6a0a819186aebbaadf9f305253fd05c5cf12ffde 1251 1246 2023-01-21T21:31:58Z Rexroom 5688 Loppupisteytys wikitext text/x-wiki Avaruussivilisaatiopeli 2–4 pelaajalle, jossa tehdään avaruustutkimusta ja käydään läpi "teknologiapuuta". == Ikonit == [SHIP]/[SUPPLY]/[POPULATION]: Pelaajakuutioita. Sinun [SUPPLY] odottavat pelaajalaudallasi ennen muuttamista [SHIP] tai [POPULATION]. [SUPPLY][ARROW][POPULATION]: Muunna yksi korikuutio populaatiokuutioksi. [ORE]: Malmia. Näet nykyisen malmivarantosi pelaajapaneelistasi. [LOSE_POPULATION]: Menetä populaatiokuutio (maksuna jostain toiminnosta). [I]/[II]/[III]/[IIII]: Tason 1/2/3/4-teknologia. [RESEARCH][TECHX]: Tutki teknologia. Vaatii [POPULATION]. [VP]: Voittopisteitä. [COLONIZE]: Kolonisoi planeettakunta. Sinun on hallittava se ja pitää siellä vaaditun määrän alusvoimaa. == Teknologia-alat == Pelissä on 4 erinäistä teknologia-alaa, joilla on omat erityispiirteensä. Jotkut tutkittavat teknologiat ovat kahden alan hybridejä. * Kaupankäynti – kolonisointitoiminnot, liikkumiset * Sotatekniikka – alusrakennus, liikkumiset, parantelut * Talous – ruoantuotantoautomatisointi, väestönkasvu, malmintuotanto * Tiede – malmintuotantoautomatisointi, tutkimustoiminnot, malmibonukset == Pelin kulku == Pelaajan vuorossa on kolme vaihetta: 1. Toimintavaihe: aseta työläisesi vapaaseen toimintoheksaan ja maksa mahdolliset kulut. [I]/[II]/[III]/[IIII]-korteissa olevat toiminnot vaativat, että kyseistä teknologiaa on tutkittu pelaajan toimesta. 2. Tuotantovaihe: voit joko a) [POPULATION] kasvattaa väestöä, b) [ORE] tuottaa malmia tai c) tehdä resurssivaihtoa. Väestönkasvussa jokaisesta sarakkeesta, jonka alla ei ole ruoantuotantolevy, muuttuu 1 korikuutio populaatioksi. (Huom. Jos sarakkeessa ei ole kuutioita, et saa sieltä populaatiota.) Malmintuotannossa jokainen paljas malmisymboli malmintuotantoradalla antaa sinulle yhden malmin. 3. Saavutusvaihe: tarkista, onko pelaaja saavuttanut jonkun saavutuksista ja valitsee sitten '''yhden''' vuoronsa aikana == Pelin päättyminen == Kun 2–3-pelaajan pelissä on otettu 3 saavutusta tai 4-pelaajan pelissä 4 saavutusta jonkun pelaajan vuoron lopussa, kierros pelataan normaalisti loppuun, jonka jälkeen pelataan vielä viimeinen kierros alkaen aloituspelaajasta. Tämän jälkeen alkaa loppupisteytys. Eniten voittopisteitä kerännyt pelaaja voittaa pelin. Tasapelit ratkaistaan ensin pelaajien tuotantotasojen summan mukaan, toisena kenellä on eniten populaatiokuutioita ja viimeisenä ratkaisijana malmimäärän mukaan. === Loppupisteytys === Pelin lopussa suoritetaan loppupisteytys. Valtaosa pisteistä lasketaan jo pelin aikana. {| class="wikitable" style="text-align: center" !Kohde !Voittopisteitä |- |[I] |1 per teknologia |- |[II] |2 per teknologia |- |[III] |3 per teknologia |- |Yksityiset teknologiat |2–3 riippuen tasosta |- |[IIII] |määritelty kortissa |- |Automaatiorata |pisteitä peitetyistä koloista |- |Kolosinoidut planeettakunnat |merkitty kolonisoidussa kortissa |- |Hallitut planeettakunnat<br>(myös telakkakunnat) |1 per kohde |- |Ei-hallittavat kohteet<br>(Sol ja ulkoavaruus) |1 per kohde<br>(sotavoiman enemmistö, tasapeleissä jaettu) |- |Tapahtumakortit |merkitty tapahtumakorteissa |- |Saavutukset |merkitty saavutuksissa |} Tasatilanteissa vähiten [FOOD_DISC]/[ORE_DISC] pelaajalaudalla pitävä pelaaja voittaa. Toinen ratkaisija on eniten [POPULATION] ja kolmantena eniten [ORE]. Jos tasatilanne jatkuu vielä, niin pelaajat päätyvät tasapeliin. f58bae386a13368ab0a686c5bc7fb9d08092561c 1252 1251 2023-01-22T17:37:11Z Rexroom 5688 Poista toisto wikitext text/x-wiki Avaruussivilisaatiopeli 2–4 pelaajalle, jossa tehdään avaruustutkimusta ja käydään läpi "teknologiapuuta". == Ikonit == [SHIP]/[SUPPLY]/[POPULATION]: Pelaajakuutioita. Sinun [SUPPLY] odottavat pelaajalaudallasi ennen muuttamista [SHIP] tai [POPULATION]. [SUPPLY][ARROW][POPULATION]: Muunna yksi korikuutio populaatiokuutioksi. [ORE]: Malmia. Näet nykyisen malmivarantosi pelaajapaneelistasi. [LOSE_POPULATION]: Menetä populaatiokuutio (maksuna jostain toiminnosta). [I]/[II]/[III]/[IIII]: Tason 1/2/3/4-teknologia. [RESEARCH][TECHX]: Tutki teknologia. Vaatii [POPULATION]. [VP]: Voittopisteitä. [COLONIZE]: Kolonisoi planeettakunta. Sinun on hallittava se ja pitää siellä vaaditun määrän alusvoimaa. == Teknologia-alat == Pelissä on 4 erinäistä teknologia-alaa, joilla on omat erityispiirteensä. Jotkut tutkittavat teknologiat ovat kahden alan hybridejä. * Kaupankäynti – kolonisointitoiminnot, liikkumiset * Sotatekniikka – alusrakennus, liikkumiset, parantelut * Talous – ruoantuotantoautomatisointi, väestönkasvu, malmintuotanto * Tiede – malmintuotantoautomatisointi, tutkimustoiminnot, malmibonukset == Pelin kulku == Pelaajan vuorossa on kolme vaihetta: 1. Toimintavaihe: aseta työläisesi vapaaseen toimintoheksaan ja maksa mahdolliset kulut. [I]/[II]/[III]/[IIII]-korteissa olevat toiminnot vaativat, että kyseistä teknologiaa on tutkittu pelaajan toimesta. 2. Tuotantovaihe: voit joko a) [POPULATION] kasvattaa väestöä, b) [ORE] tuottaa malmia tai c) tehdä resurssivaihtoa. Väestönkasvussa jokaisesta sarakkeesta, jonka alla ei ole ruoantuotantolevy, muuttuu 1 korikuutio populaatioksi. (Huom. Jos sarakkeessa ei ole kuutioita, et saa sieltä populaatiota.) Malmintuotannossa jokainen paljas malmisymboli malmintuotantoradalla antaa sinulle yhden malmin. 3. Saavutusvaihe: tarkista, onko pelaaja saavuttanut jonkun saavutuksista ja valitsee sitten '''yhden''' vuoronsa aikana == Pelin päättyminen == Kun 2–3-pelaajan pelissä on otettu 3 saavutusta tai 4-pelaajan pelissä 4 saavutusta jonkun pelaajan vuoron lopussa, kierros pelataan normaalisti loppuun, jonka jälkeen pelataan vielä viimeinen kierros alkaen aloituspelaajasta. Tämän jälkeen alkaa loppupisteytys. === Loppupisteytys === Pelin lopussa suoritetaan loppupisteytys. Valtaosa pisteistä lasketaan jo pelin aikana. {| class="wikitable" style="text-align: center" !Kohde !Voittopisteitä |- |[I] |1 per teknologia |- |[II] |2 per teknologia |- |[III] |3 per teknologia |- |Yksityiset teknologiat |2–3 riippuen tasosta |- |[IIII] |määritelty kortissa |- |Automaatiorata |pisteitä peitetyistä koloista |- |Kolosinoidut planeettakunnat |merkitty kolonisoituun korttiin |- |Hallitut planeettakunnat<br>(myös telakkakunnat) |1 per kohde |- |Ei-hallittavat kohteet<br>(Sol ja ulkoavaruus) |1 per kohde<br>(sotavoiman enemmistö, tasapeleissä jaettu) |- |Tapahtumakortit |merkitty tapahtumakortteihin |- |Saavutukset |merkitty saavutuksiin |} Tasatilanteissa vähiten [FOOD_DISC]/[ORE_DISC] pelaajalaudalla pitävä pelaaja voittaa. Toinen ratkaisija on eniten [POPULATION] ja viimeisenä eniten [ORE]. Jos tasatilanne pysyy samana, niin pelaajat päätyvät tasapeliin. 5c72e404145c87c590ab2bc984acfa0df416be9c Tips beyondthesun 0 206 1247 2023-01-14T10:48:56Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki = Suunnittelijan vinkit = Koska teknologiapuu muuttuu helposti, pelaajien strategioiden pitäisi mukautua sieltä nousevien teknologioiden mukana. Yksi peli voi suosia tutkimusta, toinen siirtokuntien perustamista tai kolmas kumpaakin. Kun valitset uuden teknologian, tarkista, että se palvelee sekä lyhyen ajan tarpeita (välittömillä bonuksilla) ja pitkän ajan päämääriä (uusilla toiminnoilla). Siirtokuntien perustaminen voi vaikuttaa raskaalta työltä, koska se sisältää siirtelyä, hallinnan ottoa ja avaruusaluksien palauttamista, mutta se voi olla tärkeä osa voittavaa strategiaa, eikä siten ole syytä jättää vähemmälle huomiolle. Jos kilpailu planeettakuntien hallitsemista näyttää kuumenevan, voi olla hyvä hetki hengähtää ja siirtyä tekemään tutkimusta sen ajan. Kun kilpailijasi ovat kuluttaneet sotavoimiaan kolonisoinnista, on aika poimia halvalla hallintaasi planeettakuntia. Populaatiokuutioita voi jäädä "jumiin" teknologioihin tai liian suureen laivueeseen. Vapauttaaksesi lisää populaatiokuutioita, joko nosta ruoantuotantoasi, kolonisoi planeettakuntia tai käytä toimintoja, jotka käyttävät varantokuutioita suoraan. e77cbf672005825096a1bea5cb3655a89412be1c 1248 1247 2023-01-16T22:09:51Z Rexroom 5688 Tekstin puhtaaksikirjoitus wikitext text/x-wiki = Suunnittelijan vinkit = Koska teknologiapuu on alati altis muutoksille, pelaajien strategioiden pitäisi mukautua sieltä nousevien teknologioiden mukana. Yksi peli voi suosia tutkimusta, toinen siirtokuntien perustamista tai kolmas kumpaakin. Kun valitset uuden teknologian, tarkista, että se palvelee sekä lyhyen ajan tarpeita (välittömillä bonuksilla) ja pitkän ajan päämääriä (uusilla toiminnoilla). Siirtokuntien perustaminen voi vaikuttaa raskaalta työltä, koska se sisältää siirtelyä, hallinnan ottoa ja avaruusaluksien palauttamista, mutta se voi olla tärkeä osa voittavaa strategiaa, eikä siten ole syytä jättää vähemmälle huomiolle. Jos kilpailu planeettakuntien hallitsemisesta näyttää kuumenevan, voi olla hyvä hetki hengähtää ja siirtyä tekemään tutkimusta sen ajan. Kun kilpailijasi ovat kuluttaneet sotavoimiaan kolonisoinnista, on aika tehdä siirto ja ottaa halvalla hallintaasi planeettakuntia. Populaatiokuutioita voi jäädä "jumiin" teknologioihin tai liian suureen laivueeseen. Vapauttaaksesi lisää populaatiokuutioita, joko nosta ruoantuotantoasi, kolonisoi planeettakuntia tai käytä toimintoja, jotka hyödyntävät varantokuutioita suoraan. 446c8409756ff532ccf1423fd91afe7700be54f3 1253 1248 2023-01-22T21:04:41Z Rexroom 5688 wikitext text/x-wiki = Suunnittelijan vinkit = Koska teknologiapuu on alati altis muutoksille, pelaajien strategioiden pitäisi mukautua sieltä nousevien teknologioiden mukana. Yksi peli voi suosia tutkimusta, toinen siirtokuntien perustamista tai kolmas kumpaakin. Kun valitset uuden teknologian, tarkista, että se palvelee sekä lyhyen ajan tarpeita (välittömillä bonuksilla) ja pitkän ajan päämääriä (uusilla toiminnoilla). Siirtokuntien perustaminen voi vaikuttaa raskaalta työltä, koska se sisältää siirtelyä, hallinnan ottoa ja avaruusaluksien palauttamista, mutta se voi olla tärkeä osa voittavaa strategiaa, eikä siten ole syytä jättää vähemmälle huomiolle. Jos kilpailu planeettakuntien hallitsemisesta näyttää kuumenevan, voi olla hyvä hetki hengähtää ja siirtyä tekemään tutkimusta sen ajan. Kun kilpailijasi ovat kuluttaneet sotavoimiaan kolonisoinnista, on aika tehdä siirto ja ottaa halvalla hallintaasi planeettakuntia. Populaatiokuutioita voi jäädä "jumiin" teknologioihin tai liian suureen laivueeseen. Vapauttaaksesi lisää populaatiokuutioita, joko nosta ruoantuotantoasi, kolonisoi planeettakuntia tai käytä toimintoja, jotka hyödyntävät suoraan kuutioitasi pelaajalaudalta. == Teknologiaryhmät == === [II]-tason === Tiede * Hypertietokoneet (sininen/sininen, sininen) * Ihmiskokeet (sininen/punainen, sininen) * Psioniikan tutkimus (sininen/vihreä, sininen) * Monisiirtokunnallinen symposiumi (sininen/keltainen, sininen) Sotatekniikka * Eksometalliseoksen jalostus (punainen/sininen, punainen) * Plasmakanuuna (punainen/punainen, punainen) * Androidihävittäjät (punainen/vihreä, punainen) * Modulaarinen laivasto (punainen/keltainen, punainen) Talous * Biosynaptinen verkosto (vihreä/sininen, vihreä) * Ulkosektorin miliisi (vihreä/punainen, vihreä) * Massakloonaus (vihreä/vihreä, vihreä) * Androidimaanmittaajat (vihreä/keltainen, vihreä) Kaupankäynti * Avaruusaika-anomalian tutkimus (keltainen/sininen, keltainen) * Terranova-risteilijät (keltainen/punainen, keltainen) * Huoltoetuvartiosto (keltainen/vihreä, keltainen) * Planeettakuntien välinen diplomatia (keltainen/keltainen, keltainen) === [III]-tason === === [IIII]-tason === dd0e238130054ff78bb3a038d7f442bd7cdccfb3 Gamehelpsaintpetersburg 0 207 1249 2023-01-19T22:42:03Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Pelaajat rakentavat Pietarista "Idän Pariisia". Eniten voittopisteitä kerännyt voittaa. == Alkuasettelut == Pelaajille jaetaan 25 ruplaa alussa ja pelaajamäärästä riippuen 1–2 aloituspelaajamerkkiä. Pelilaudan korttipaikkoihin nostetaan myös työläispakasta pelaajamäärä riippuen tietty määrä kortteja. == Pelin kulku == Pelin kierroksen aikana käydään läpi neljä vaihetta, jotka ovat pelilaudalla näkyvät neljä korttipakkaa: * Työläisvaihe – vihreät kortit tuottavat ruplia ja voittopisteitä * Rakennusvaihe – siniset kortit tuottavat ruplia ja voittopisteitä * Aristokraattivaihe – punaiset kortit tuottavat ruplia ja voittopisteitä * Kauppavaihe – tämän vaiheen lopulla ei tule ruplia ja pisteitä Vaihe aloitetaan nostamalla kortteja tyhjiin paikkoihin. (Huomaa, että alarivin kortit katsotaan myös täyttävän ylärivin korttien paikkoja.) Jokaisen vaiheen aloittaa vastaavan aloituspelaajamerkin omaava pelaaja, ja vuorot kiertävät myötäpäivään. === Pelaajan toiminnot === Pelaaja voi suorittaa vaiheen aikana yhden neljästä toiminnosta: hän voi '''ostaa''' kortin, '''lisätä''' kortin käteen, '''pöydätä''' kortin kädestä tai '''ohittaa vuoronsa'''. Voit '''ostaa''' kortin maksamalla kortin hinnan verran ruplia. Kortin hinta näkyy kortin vasemmassa yläkulmassa. Jos sinulla on jo samoja kortteja edessäsi, saat niiden verran alennusta kortin hintaan. Alarivin kortit maksavat 1 ruplan vähemmän. '''Jokaisesta kortista pitää maksaa vähintään 1 rupla.''' Voit '''lisätä''' kortin käteesi. Pelaajien käsiraja on kolme korttia. Voit '''pöydätä''' kortin kädestäsi ja asettaa sen eteesi muiden korttiesi joukkoon. Maksa kortista samalla tavalla, kuin olisit ostamassa sitä. Voit '''ohittaa vuorosi'''. Vaihe päättyy, kun kaikki ovat ohittaneet vuoronsa vuoronperään. === Kauppakortit === Kauppakortit nostetaan kauppavaiheen pakasta, ja ne tunnistaa kortin hinnan ympärillä olevasta laatikosta. Kauppakortti ostetaan korvaamalla pöydällä oleva korttisi kauppakortilla, kunhan se on samaa väriä. Vähennä kauppakortin hinnasta korvattavan kortin hinta. Työläiskorteissa on myös huomioitava, mitä tyyppiä ne on. === Pisteytys === Kun vaihe päättyy, pisteytetään sen vaiheen kortit. (Kyseisen vaiheen kortit näkyvät sivuttain käännetystä pakasta.) Ruplia saa kolikoista, voittopisteitä kilvistä. '''Kauppavaiheen lopussa ei tehdä pisteytystä.''' === Kierroksen loppu === Kun kaikki neljä vaihetta on käyty läpi, kierros on päättynyt. Alariviltä siirretään jäljellejääneet kortit poistopinoon, ja ylärivin kortit siirretään alempaan riviin. Aloituspelaajien merkit kiertävät myötäpäivään, ja uusi kierros alkaa. == Pelin loppu ja loppupisteytys == Kun joku korttipakoista ehtyy, alkaa viimeinen kierros. Pelatkaa peli loppuun normaalisti. Tämän jälkeen alkaa lopullinen pisteytys. * Jokaisesta 10 ruplasta tulee 1 piste. * Jokaisesta käsikortista tulee -5 pistettä. Jokaisesta '''erilaisesta''' aristokraatista tulee pisteitä tietty määrä. Eniten pisteitä kerännyt pelaaja voittaa. Tasapeleissä ruplat ratkaisevat. 86e5f725f7ba6c7982ed7c3cdf243bd5f5664d96 Tips saintpetersburg 0 208 1250 2023-01-20T16:49:25Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki '''Ensimmäisellä kierroksella jokaisen pelaajan pitäisi ostaa kaksi työläistä!''' Pelaaja, jolla on vähemmän kuin kaksi työläistä, tulee olemaan pahasti jäljessä muista. Työläisillä on paras hinta/tuotto-suhde. '''Kalleimmilla korteilla on isommat hyötysuhteet.''' 1 piste markkinasta maksaa 5 ruplaa, 1 piste tullikamarista maksaa vain 4 ruplaa jne. Täten pelaaja voi hyvin säästää rahansa kalleimpiin sijoituksiin. Ensimmäisessä rakennusvaiheessa tulee esiin kysymys: '''kannattaako rakentaa kalliita rakennuksia?''' Kyseiset rakennukset tuottavat paljon pisteitä rakennusvaiheen lopun pisteytyksien aikana, mutta pelaajalle jää vähän rahaa käteen, ja voi olla siten vaarallista. '''Kauppakortit ovat yleensä hyödyllisiä.''' Pelaajien kannattaisi säästää rahaa kauppavaiheeseen. Mutta pelaajien on pidettävä huolta käsivarannostaan, koska tässä vaiheessa ei ole pisteytystä. Varaton pelaaja ei kykenisi ostamaan työläisvaiheessa uusia työläisiä. Kun pelaaja miettii, haluaako ottaa kortin käteensä vai ei, hänen on mietittävä mahdollisten pisteiden ja ruplien myös seuraavaa: kun kortti poistetaan laudalta, se tekee tilaa uusille korteille seuraavassa vaiheessa. Haluaako pelaaja tätä? Kun pelaaja on seuraavan vaiheen aloituspelaaja, häntä ei kiinnostaisi '''jättää tilaa uusille kortteille'''. Toisaalta kauimpana aloituspelaajasta istuva pelaajaa kiinnostaisi nähdä enemmän kortteja. Kortin lisääminen käteen on usein tärkeä siirto. Tällä tavalla pelaaja voi ostaa kortin myöhemmässä vaiheessa, kun hänellä on tarpeeksi rahaa. Vaarana on tietenkin liika spekulointi, ja mikään ei ole karvasta kuin käteen jääneet kortit pelin lopussa, jotka antavat sitten miinuspisteitä. Saint Petersburg on peli, jossa on '''pysyvästi rajoittunut rahamäärä'''. Tämä on hyvästä, koska peli olisi tylsä, jos kaikilla pelaajilla olisi rutkasti rahaa korttien osteluun. c65e9f7eb50587ac08f116360fc2fcef4a4fb1fb Tips beyondthesun 0 206 1254 1253 2023-01-22T21:15:58Z Rexroom 5688 wikitext text/x-wiki = Suunnittelijan vinkit = Koska teknologiapuu on alati altis muutoksille, pelaajien strategioiden pitäisi mukautua sieltä nousevien teknologioiden mukana. Yksi peli voi suosia tutkimusta, toinen siirtokuntien perustamista tai kolmas kumpaakin. Kun valitset uuden teknologian, tarkista, että se palvelee sekä lyhyen ajan tarpeita (välittömillä bonuksilla) ja pitkän ajan päämääriä (uusilla toiminnoilla). Siirtokuntien perustaminen voi vaikuttaa raskaalta työltä, koska se sisältää siirtelyä, hallinnan ottoa ja avaruusaluksien palauttamista, mutta se voi olla tärkeä osa voittavaa strategiaa, eikä siten ole syytä jättää vähemmälle huomiolle. Jos kilpailu planeettakuntien hallitsemisesta näyttää kuumenevan, voi olla hyvä hetki hengähtää ja siirtyä tekemään tutkimusta sen ajan. Kun kilpailijasi ovat kuluttaneet sotavoimiaan kolonisoinnista, on aika tehdä siirto ja ottaa halvalla hallintaasi planeettakuntia. Populaatiokuutioita voi jäädä "jumiin" teknologioihin tai liian suureen laivueeseen. Vapauttaaksesi lisää populaatiokuutioita, joko nosta ruoantuotantoasi, kolonisoi planeettakuntia tai käytä toimintoja, jotka hyödyntävät suoraan kuutioitasi pelaajalaudalta. = Teknologiaryhmät = == 2-tason == === Tiede === * Hypertietokoneet (sininen/sininen, sininen) * Ihmiskokeet (sininen/punainen, sininen) * Psioniikan tutkimus (sininen/vihreä, sininen) * Monisiirtokunnallinen symposiumi (sininen/keltainen, sininen) === Sotatekniikka === * Eksometalliseoksen jalostus (punainen/sininen, punainen) * Plasmakanuuna (punainen/punainen, punainen) * Androidihävittäjät (punainen/vihreä, punainen) * Modulaarinen laivasto (punainen/keltainen, punainen) === Talous === * Biosynaptinen verkosto (vihreä/sininen, vihreä) * Ulkosektorin miliisi (vihreä/punainen, vihreä) * Massakloonaus (vihreä/vihreä, vihreä) * Androidimaanmittaajat (vihreä/keltainen, vihreä) === Kaupankäynti === * Avaruusaika-anomalian tutkimus (keltainen/sininen, keltainen) * Terranova-risteilijät (keltainen/punainen, keltainen) * Huoltoetuvartiosto (keltainen/vihreä, keltainen) * Planeettakuntien välinen diplomatia (keltainen/keltainen, keltainen) == 3-tason == == 4-tason == c81ba489f867f5a064a0e39d93e984ca411dfb6f 1255 1254 2023-01-23T17:51:21Z Rexroom 5688 wikitext text/x-wiki = Suunnittelijan vinkit = Koska teknologiapuu on alati altis muutoksille, pelaajien strategioiden pitäisi mukautua sieltä nousevien teknologioiden mukana. Yksi peli voi suosia tutkimusta, toinen siirtokuntien perustamista tai kolmas kumpaakin. Kun valitset uuden teknologian, tarkista, että se palvelee sekä lyhyen ajan tarpeita (välittömillä bonuksilla) ja pitkän ajan päämääriä (uusilla toiminnoilla). Siirtokuntien perustaminen voi vaikuttaa raskaalta työltä, koska se sisältää siirtelyä, hallinnan ottoa ja avaruusaluksien palauttamista, mutta se voi olla tärkeä osa voittavaa strategiaa, eikä siten ole syytä jättää vähemmälle huomiolle. Jos kilpailu planeettakuntien hallitsemisesta näyttää kuumenevan, voi olla hyvä hetki hengähtää ja siirtyä tekemään tutkimusta sen ajan. Kun kilpailijasi ovat kuluttaneet sotavoimiaan kolonisoinnista, on aika tehdä siirto ja ottaa halvalla hallintaasi planeettakuntia. Populaatiokuutioita voi jäädä "jumiin" teknologioihin tai liian suureen laivueeseen. Vapauttaaksesi lisää populaatiokuutioita, joko nosta ruoantuotantoasi, kolonisoi planeettakuntia tai käytä toimintoja, jotka hyödyntävät suoraan kuutioitasi pelaajalaudalta. = Teknologiaryhmät = == 2-tason == === Tiede === * Hypertietokoneet (sininen/sininen) * Ihmiskokeet (sininen/punainen) * Psioniikan tutkimus (sininen/vihreä) * Monisiirtokunnallinen symposiumi (sininen/keltainen) === Sotatekniikka === * Eksometalliseoksen jalostus (punainen/sininen) * Plasmakanuuna (punainen/punainen) * Androidihävittäjät (punainen/vihreä) * Modulaarinen laivasto (punainen/keltainen) === Talous === * Biosynaptinen verkosto (vihreä/sininen) * Ulkosektorin miliisi (vihreä/punainen) * Massakloonaus (vihreä/vihreä) * Androidimaanmittaajat (vihreä/keltainen) === Kaupankäynti === * Avaruusaika-anomalian tutkimus (keltainen/sininen) * Terranova-risteilijät (keltainen/punainen) * Huoltoetuvartiosto (keltainen/vihreä) * Planeettakuntien välinen diplomatia (keltainen/keltainen) == 3-tason == === Tiede === * Transuloitteinen tekoäly (sininen/sininen) * Planeetanlaajuinen tutkimus (sininen/punainen) * Metaihmiskammiot (sininen/vihreä) * Kekomielitietokone (sininen/keltainen) === Sotatekniikka === * Ulkoavaruuden megastruktuurit (punainen/sininen) * Pääalus (punainen/punainen) * Androidiarmada (punainen/vihreä) * Palkkalaivue (punainen/keltainen) === Talous === * Aivoimplantit (vihreä/sininen) * Orbitaalien valmistus (vihreä/punainen) * Androidioikeusliike (vihreä/vihreä) * Subatominen valmistus (vihreä/keltainen) === Kaupankäynti === * Kvanttidatavaihde (keltainen/sininen) * Hyperajon tehostimet (keltainen/punainen) * Madonreikä-teknologia (keltainen/vihreä) * Salamaankaltaistajat (keltainen/keltainen) == 4-tason == * Teknologinen singulariteetti (sininen/sininen) * Gigakorporaatiot (sininen/vihreä) * Rinnakkaisuniversumigeneraattori (sininen/keltainen) * Uusteollinen vallankumous (punainen/sininen) * Megalaivueen rakennus (punainen/punainen) * Mieli/kone-assimilaatio (vihreä/punainen) * Uusi kulta-aika (vihreä/vihreä) * Galaktinen imperiumi (keltainen/punainen) * Androidien itsenäisyys (keltainen/vihreä) * Siirtokuntaministeriö (keltainen/keltainen) e5b16c80661948ba32331aca92d8a0f2938105b9 Tips escapefromthehiddencastle 0 209 1256 2023-02-06T20:35:51Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Muista, ettet voi astua huoneisiin kuin vasta silloin, kun Hugo saapuu galleriaan. Sijoita siten vieraasi kohtiin, jossa heillä on riittävästi aikaa siirtyä turvaan niin vaatiessa. == Matematiikkaa == Todennäköisyys, että heitetään Hugoa: 1/6 Todennäköisyys, että heitetään Hugoja peräkkäin: {| class="wikitable" !colspan="10"|Hugon todennäköisyys = 1 / 6^n |- !1 !2 !3 !4 !5 !6 !7 !8 !9 !10 |- |1 / 6 |1 / 36 |1 / 216 |1 / 1 296 |1 / 7 776 |1 / 46 656 |1 / 279 936 |1 / 1 679 616 |1 / 10 077 696 |1 / 60 466 176 |} b602f7b9ee6fd0c8ecbab54163c9f7c07532ad0d 1257 1256 2023-02-14T14:13:02Z Rexroom 5688 Korjaus matematiikassa wikitext text/x-wiki Muista, ettet voi astua huoneisiin ennen Hugon saapumista galleriaan. Sijoita siten vieraasi kohtiin, jossa heillä on riittävästi aikaa siirtyä turvaan niin vaatiessa. == Matematiikkaa == Todennäköisyys, että nopan heiton tuloksena on Hugo: 2/6 = 1/3 Todennäköisyys, että nopan heittojen tuloksena on Hugoja peräkkäin: {| class="wikitable" !colspan="10"|Hugon todennäköisyys = 1 / 3^n (n = kertoja peräkkäin) |- !1 !2 !3 !4 !5 !6 !7 !8 !9 !10 |- |1 / 3 |1 / 9 |1 / 27 |1 / 81 |1 / 243 |1 / 729 |1 / 2 187 |1 / 6 561 |1 / 19 683 |1 / 59 049 |} 6ab875ace1c6e90873adc14ff8d7cc365fb62709 Gamehelpkoikoi 0 179 1258 1176 2023-02-15T09:46:31Z Rexroom 5688 wikitext text/x-wiki Koi-koi on japanilainen keräilypeli kahdelle pelaajalle, joka pelataan japanilaisella Hanafuda-korteilla. == Hanafuda-pakka == Hanafudan 48 korttien kokonaisuudessa maat jakaantuvat 12 eri kuukauteen tai kasviin. Jokaisella kuukaudella on 1-3 eri hahmoa tai esinettä, jotka luokitellaan kortteina '''kirkkaiksi''', '''eläimiksi''' ja '''nauhoiksi'''. Muut kortit luokitellaan '''akanoiksi'''. == Pelin kulku == Kyseessä on keräilypeliksi luokiteltava korttipeli, jossa yritetään rakennella eri Yaku-yhdistelmiä pisteiden toivossa. Pelaajat voivat pelata joko 6 tai 12 "kuukautta" (peliä). Pelin alussa pelaajille ja pelialueelle jaetaan 8 korttia. Jäljellejäävät kortit asetetaan nostopakaksi pelialueen viereen. Kättä ei täydennetä uusilla korteilla pelin aikana. Jos pelaajalla on kädessään jaon jälkeen kuukauden kaikki neljä korttia, hän voittaa sen pelin ja saa 6 pistettä. Pelaaja pelaa yhden kortin kädestään. Jos pelattu kortti on paria pelialueen kortin maan kanssa, se pöydätään pelaajan eteen. Jos pelialueella on kolme yhden kuukauden korttia, neljäs pelattava kortti pöytää ne kaikki. Muussa tapauksessa pelattu kortti jätetään pelialueelle. Sen jälkeen nostetaan pakasta kortti, joka pelataan heti kuin se olisi pelaajan pelaama kortti. Sitten vuoro siirtyy vastustajalle. Pelaajan saadessaan valmiiksi jonkun Yakuista vuoronsa lopussa, pelaaja voi joko jatkaa peliä sanomalla "koi" tai lopettaa sen. Peli loppuu käsien tyhjennettyä. Jos kukaan ei ole saanut rakennettua yakua, jakaja saa 6 pistettä. == Yakut == Korteista saatavia yhdistelmiä voi katsoa pelin aikana "Voittamasi kortit" tekstilaatikon viereltä löytyvää ?-merkkiä napsauttamalla, joka listaa pelin korttiyhdistelmiä eri pisteytyksillä. Huomioi, että jotkut yakut ovat kasvavia, eli jos saat lisättyä yhden enemmän yhteen ryhmään, pistemääräsi lisäkorttien verran. == Muunnelmat == Pelistä on olemassa lukuisia muunnelmia, mutta BGA:n kannalta tärkeimmät ovat tässä: === Pehmeä Koi === Alussa pistekerroin on 1, mutta jos pelialueella jaon jälkeen näkyy kirkkaita, pistekerrointa nostetaan molemmille pelaajalle näkyvien kirkkaiden verran. Jos pelaaja sanoo "koi", ''pelaajan'' pistekerrointa nostetaan yhdellä. === Kova Koi === Jos pelaaja sanoo "koi", hänen ''vastustajansa'' tuplaa pisteensä jos hän onnistuu rakentamaan yakun. Jos pelaaja kerää 7 pistettä tai yli pelin aikana, hänen pisteensä tuplataan. Kummatkin tuplaukset pätevät, eli voit saada nelinkertaisen tuloksen. === "Katselemassa"-yakut === Tässä muunnelmassa voidaan pelata kahden kortin yakuja. '''Sade pilaa juhlat''' -muunnelmassa pöydätetty marraskuun salama-kortti estää kahden kortin yakujen pisteytykset. '''Peruskerroin kirkkaista''' -asetuksessa kuukauden alussa pöydälle jaettujen kirkkaiden määrä määrittää kuukauden peruskertoimen, eli kolme kirkasta antaa pisteiden kertoimeksi kolmen. 719582f8084e5f5c5c1d07ed4ab0319948f7cafe 1259 1258 2023-02-15T09:47:02Z Rexroom 5688 wikitext text/x-wiki Koi-koi on japanilainen keräilypeli kahdelle pelaajalle, jota pelataan japanilaisella Hanafuda-korteilla. == Hanafuda-pakka == Hanafudan 48 korttien kokonaisuudessa maat jakaantuvat 12 eri kuukauteen tai kasviin. Jokaisella kuukaudella on 1-3 eri hahmoa tai esinettä, jotka luokitellaan kortteina '''kirkkaiksi''', '''eläimiksi''' ja '''nauhoiksi'''. Muut kortit luokitellaan '''akanoiksi'''. == Pelin kulku == Kyseessä on keräilypeliksi luokiteltava korttipeli, jossa yritetään rakennella eri Yaku-yhdistelmiä pisteiden toivossa. Pelaajat voivat pelata joko 6 tai 12 "kuukautta" (peliä). Pelin alussa pelaajille ja pelialueelle jaetaan 8 korttia. Jäljellejäävät kortit asetetaan nostopakaksi pelialueen viereen. Kättä ei täydennetä uusilla korteilla pelin aikana. Jos pelaajalla on kädessään jaon jälkeen kuukauden kaikki neljä korttia, hän voittaa sen pelin ja saa 6 pistettä. Pelaaja pelaa yhden kortin kädestään. Jos pelattu kortti on paria pelialueen kortin maan kanssa, se pöydätään pelaajan eteen. Jos pelialueella on kolme yhden kuukauden korttia, neljäs pelattava kortti pöytää ne kaikki. Muussa tapauksessa pelattu kortti jätetään pelialueelle. Sen jälkeen nostetaan pakasta kortti, joka pelataan heti kuin se olisi pelaajan pelaama kortti. Sitten vuoro siirtyy vastustajalle. Pelaajan saadessaan valmiiksi jonkun Yakuista vuoronsa lopussa, pelaaja voi joko jatkaa peliä sanomalla "koi" tai lopettaa sen. Peli loppuu käsien tyhjennettyä. Jos kukaan ei ole saanut rakennettua yakua, jakaja saa 6 pistettä. == Yakut == Korteista saatavia yhdistelmiä voi katsoa pelin aikana "Voittamasi kortit" tekstilaatikon viereltä löytyvää ?-merkkiä napsauttamalla, joka listaa pelin korttiyhdistelmiä eri pisteytyksillä. Huomioi, että jotkut yakut ovat kasvavia, eli jos saat lisättyä yhden enemmän yhteen ryhmään, pistemääräsi lisäkorttien verran. == Muunnelmat == Pelistä on olemassa lukuisia muunnelmia, mutta BGA:n kannalta tärkeimmät ovat tässä: === Pehmeä Koi === Alussa pistekerroin on 1, mutta jos pelialueella jaon jälkeen näkyy kirkkaita, pistekerrointa nostetaan molemmille pelaajalle näkyvien kirkkaiden verran. Jos pelaaja sanoo "koi", ''pelaajan'' pistekerrointa nostetaan yhdellä. === Kova Koi === Jos pelaaja sanoo "koi", hänen ''vastustajansa'' tuplaa pisteensä jos hän onnistuu rakentamaan yakun. Jos pelaaja kerää 7 pistettä tai yli pelin aikana, hänen pisteensä tuplataan. Kummatkin tuplaukset pätevät, eli voit saada nelinkertaisen tuloksen. === "Katselemassa"-yakut === Tässä muunnelmassa voidaan pelata kahden kortin yakuja. '''Sade pilaa juhlat''' -muunnelmassa pöydätetty marraskuun salama-kortti estää kahden kortin yakujen pisteytykset. '''Peruskerroin kirkkaista''' -asetuksessa kuukauden alussa pöydälle jaettujen kirkkaiden määrä määrittää kuukauden peruskertoimen, eli kolme kirkasta antaa pisteiden kertoimeksi kolmen. 3e39f0fa1600c2fc26beb9c9a2d65e225c77a0a8 Gamehelpescapefromthehiddencastle 0 193 1260 1223 2023-02-21T17:02:34Z Rexroom 5688 Korjausta ja uusi osio wikitext text/x-wiki == Päämäärä == Pelaaja johdattaa vieraitaan Hugo-kummitukselta turvaan, joka yrittää napata heidät kauhujen kellariinsa. == Vieraiden (pelaajamerkkien) määrä == * 2 pelaajaa: 6 vierasta per pelaaja * 3 pelaajaa: 5 vierasta * 4 pelaajaa: 4 vierasta * 5 pelaajaa: 3 vierasta * 6–8 pelaajaa: 2 vierasta == Pelin kulku == === Alkuasettelu === Pelin alussa pelaajat asettelevat vieraitaan portaikkoa ympäröivään galleriakäytävälle. Tässä vaiheessa samalle ruudulle ei voi asettaa useampia vieraita. === Juhlat alkavat === Pelaajat heittävät vuorotellen noppaa. Pelaajat voivat siirrellä silmäluvun verran yhtä vieraistaan. Jos pelaaja heittää HUGOn, kummitusta siirretään oikean yläkulman huoneen osoittaman määrän ruutuja. Aina kun Hugo kiertää gallerian kokonaan, Hugon vauhti nousee 1 ruudulla. ''Kun Hugo saapuu galleriaan, pelaajat voivat siirtää vieraitaan huoneisiin.'' Huoneet luetaan yhdeksi ruuduksi. Jos huoneessa on jo joku vieras, huoneeseen vaaditaan tasanumero, jolloin hänet häädetään huoneesta. Jos huone on merkitty vihreällä kilvellä, hän vähentää kilven osoittaman luvun verran säikkypisteitä ja lisää jos se on punainen. Jos Hugo pysähtyy ruutuun tai ohittaa ruudun, jossa on vieraita, Hugo saa kyseiset vieraat kiinni. Silloin vieraat viedään kellarin portaiden alimpaan vapaaseen portaikkoon järjestyksessä, alkaen ensimmäisenä kiinnijäänneet. Jos ruudussa oli useampia vieraita, heidät kohdellaan yhtenä ryhmänä ja asetetaan samaan portaikkoon. Pelaajat voivat omalla vuorollaan siirtää vieraansa pois portaikosta kuin normaalisti. Pelaajat saavat askeleessa merkityn verran säikkypisteitä. '''Huom!''' Jos portaikko on täynnä vieraista, portaikko täyttyy taas alimmasta askeleesta. == Pelin loppu == Peli päättyy kahdella tavalla: * Joku pelaajista kerää 46 pelkopistettä tai yli * HUGO on kiertänyt gallerian 6 kertaa Vähiten pelkopisteitä kerännyt voittaa. == Keskiyön kummitus == Tämä muunnelma toimii pelin vanhemman version mukaan: Peliä pelataan 3 kierrosta. Ensimmäisessä kierroksessa vieraat asetetaan galleriaan normaalisti. Kierros päättyy, kun galleriassa ei ole enää vieraita tai kaikki huoneet on varattuna. Jälkimmäisen sattuessa kaikki galleriassa olevat vieraat siirretään -2 portaalle. Tämän jälkeen suoritetaan pisteytys, jossa pelaajat saavat portaista ja huoneista pisteitä. Tämän jälkeen kaikki huoneissa olevat vieraat siirretään huoneen ulkopuolelle, sitten pelaajat sijoittavat portaikon vieraat galleriaan, alkaen alimmasta askeleesta. Jos askeleessa on enemmän kuin yksi vieras, eniten pelkopisteitä kerännyt aloittaa. Tämän jälkeen Hugo viedään takaisin portaikon alimpaan askeleeseen ja uusi kierros voi alkaa. Uuden kierroksen aloittaa eniten pelkopisteitä kerännyt pelaaja. Varatuista huoneista ei voi häätää vieraita. Ne pysyvät varattuina kierroksen loppuun saakka. Kaapatut vieraat pysyvät myöskin portaikossa kierroksen loppuun saakka. Niin kauan kuin ehdot ovat täyttymättä, pelaajat heittävät noppaa, vaikka hänellä ei olisi pelattavia vieraita. Hugo liikkuu aina hänen nopanheitosta 3 ruutua. 7–8 pelaajan peleissä hän liikkuu 2 ruutua. Kun kolme kierrosta on pelattu, vähiten koko pelin aikana pelkopisteitä kerännyt pelaaja voittaa. 7523889d8193fd0835848efa649c61eaf6bc4171 Gamehelpclassicgo 0 210 1261 2023-02-25T11:02:16Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki '''Go''' on klassinen kahden pelaajan abstrakti lautapeli muinaisesta Kiinasta, jossa kilpaillaan alueiden hallinnasta. == Perussäännöt == Pelaajat keräävät pisteitä hallitsemalla laudalla alueita pelin lopussa. Eniten pisteitä voittanut pelaaja voittaa. Pelaaja voi vuoronsa aikana joko pelata laudalle yhden kiven tai passata vuoronsa. Pelaaja asettaa yhdelle vapaalle laudan risteykselle kiven. Pelattu kivi pysyy paikallaan, ellei se poisteta toisen pelaajan toimesta. === Vangitseminen === Kiven tai kivien saartaminen tapahtuu niin, että kiven tai kivien kaikki vieressä olevat risteykset on tukittu vastapuolen pelaajan kivillä. Kun pelaaja saa saarrettua kiven tai ryhmän kiviä omilla kivillään, eikä saarekkeen sisällä ole tyhjiä paikkoja, hän saa vangittua saarrekkeen sisällä olevat kivet. Saarretut kivet poistetaan pelistä. == Pelin päättyminen == Kun kummatkin pelaajat passaavat vuoronperään, tarkastetaan laudalta "kuolleita" kiviä. Tällä tarkoitetaan kiviä, jotka olisi vielä kaapattavissa. Jos pelaajat eivät pääse yhteisymmärrykseen kivistä, peli jatkuu. Muutoin peli päättyy. == Pisteytys == Käytössä on kaksi eri pisteytystapaa: japanilainen ja kiinalainen. Kiinalaisessa pisteytyksessä laudasta lasketaan koko alueista, eli jokaisesta saarretusta risteyksestä ja kivestä tulee yksi piste. Japanilaisessa pisteytyksessä laudasta lasketaan vain "pellot", eli jokaisesta saarretusta risteyksestä tulee yksi piste. Vangituista kivistä tulee myös yksi piste per kivi. Jos AGA-säännöt on käytössä, näiden kahden pisteytystavan välillä ei ole eroa. === Komi ja muut tasoitukset === '''Komi''' on pelin lopussa valkoiselle annettu pistemäärä tasoittamaan mustan saamaa etua. 71288df0465d8f93942c76692ee4bec16f734b51 Gamehelpdecrypto 0 211 1262 2023-07-13T16:23:05Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Joukkueet pyrkivät viestimään omilleen numerosarjoja ilman että vastustajan joukkue saa ne siepattua. == Suomenkieliset avainsanat == Pelissä on myös avainsanat suomeksi, mutta niiden käyttöönotto vaatii valikkonavigointia. == Pelin kulku == Pelin tavoitteena on kerätä joukkueellesi 2 sieppaus-merkkiä. Jos joukkueesi kerää 2 väärä tulkinta -merkkiä, häviätte. === Kierroksen kulku === # Kummastakin joukkueesta valitaan yksi pelaajista salakoodaajaksi. # Salakoodaajat nostavat oman koodikortin. # Salakoodaajat kirjoittavat 3 vinkkiä. # Valkoisen joukkueen salakoodajan vinkit paljastetaan. # Kummatkin joukkueet miettivät valkoisen joukkueen salakoodaajan vinkkejä keskenään ja kirjoittavat ylös mahdollisen numerosarjan (valkoinen salakoodaaja seuraa vain sivusta). # Valkoinen koodikortti paljastetaan. Jos musta joukkue on oikeassa, he saavat sieppausmerkin. Jos valkoinen joukkue on väärässä, he saavat Väärä tulkinta -merkin. Muutoin mitään muuta ei tapahdu. # Toistakaa kohdat 4–6 uudestaan, mutta nyt värit vaihdettuina. (Eli valkoiset mustina ja mustat valkoisina.) === Kierroksen päättyminen === Kierroksen lopussa tarkistetaan tilanne: * Jos yhdellä joukkueella on kaksi sieppausmerkkiä, kyseinen joukkue voittaa pelin. * Jos yhdellä joukkueella on kaksi väärä tulkinta -merkkiä, kyseinen joukkue häviää pelin. Muussa tapauksessa alkaa uusi kierros. === Tasatilanteessa === Jos kummallakin joukkueella on kaksi sieppausmerkkiä, pelimerkit muutetaan pisteiksi. Sieppausmerkit antavat +1, väärä tulkinta -merkit -1. Eniten pisteitä saanut joukkue voittaa. Jos tasatilanne jatkuu, joukkueet yrittävät arvata vastajoukkueen neljä avainsanaa. Eniten oikein arvannut joukkue voittaa, muutoin kyseessä on tasapeli. == Vihjeisiin liittyvät säännöt == * '''Saat valita vihjeiden muodon,''' joten vihje voi olla yksi sana tai kokonainen virke. * Vihjeiden täytyy perustua '''yleisesti saatavilla oleviin tietoihin''', kuten suomalaisiin runoilijoihin. Et voi siten '''koskaan''' viitata yksityis- tai sisäpiiritietoihin. * Vihjeiden täytyy viitata avainsanojen '''merkitykseen'''. * Vihjeet eivät koskaan saa viitata avainsanan '''kirjoitusasuun''', '''sijaintiin''' näyttöalustalla tai '''ääntämiseen'''. Pyydettäessä '''sinun on selitettävä vihjeesi ymmärrettävästi'''. * KOSKAAN EI saa käyttää samaa vihjettä '''useammin kuin kerran samassa pelissä'''. * '''KOSKAAN EI saa lukea ääneen koodikortin koodia tai käyttää avainsanoja''' (tai niiden käännöksiä toisella kielellä) '''vihjeinä''', ei edes eri avainsanan kohdalla. 09523c29c88b2e65ec3a7aaa30ab7a247e0e28e2 1263 1262 2023-07-13T16:34:22Z Rexroom 5688 Lisäyksiä ja tarkennuksia wikitext text/x-wiki Joukkueet pyrkivät viestimään omilleen numerosarjoja ilman että vastustajan joukkue saa ne siepattua. == Suomenkieliset avainsanat == Pelissä on myös avainsanat suomeksi, mutta niiden käyttöönotto vaatii valikkonavigointia: '''Pelaa nyt > Manuaalinen > Decrypto + Pelaa nyt! > Luo > Game Language > Suomi''' Voit myös asettaa Pelaa nyt -sivun pelikohtaisista asetuksista kielipreferenssiksi suomen. Helpointa olisi kuitenkin järjestää etukäteen peli kavereiden kanssa. == Pelin kulku == Pelin tavoitteena on kerätä joukkueellesi 2 sieppaus-merkkiä. Jos joukkueesi kerää 2 väärä tulkinta -merkkiä, häviätte. === Kierroksen kulku === Kummastakin joukkueesta valitaan yksi pelaajista salakoodaajaksi. Salakoodaajat nostavat oman koodikortin. Salakoodaajat kirjoittavat 3 vinkkiä. '''Valkoisen joukkueen salakoodajan vinkit paljastetaan.''' '''Kummatkin joukkueet miettivät valkoisen joukkueen salakoodaajan vinkkejä keskenään ja kirjoittavat ylös mahdollisen numerosarjan (valkoinen salakoodaaja seuraa vain sivusta).''' '''Valkoinen koodikortti paljastetaan. Jos musta joukkue on oikeassa, he saavat sieppausmerkin. Jos valkoinen joukkue on väärässä, he saavat Väärä tulkinta -merkin. Muutoin mitään muuta ei tapahdu.''' Toistakaa korostetut uudestaan, mutta nyt värit vaihdettuina. (Eli valkoiset mustina ja mustat valkoisina.) === Kierroksen päättyminen === Kierroksen lopussa tarkistetaan tilanne: * Jos yhdellä joukkueella on kaksi sieppausmerkkiä, kyseinen joukkue voittaa pelin. * Jos yhdellä joukkueella on kaksi väärä tulkinta -merkkiä, kyseinen joukkue häviää pelin. Muussa tapauksessa alkaa uusi kierros. === Tasatilanteessa === Jos kummallakin joukkueella on kaksi sieppausmerkkiä, pelimerkit muutetaan pisteiksi. Sieppausmerkit antavat +1, väärä tulkinta -merkit -1. Eniten pisteitä saanut joukkue voittaa. Jos tasatilanne jatkuu, joukkueet yrittävät arvata vastajoukkueen neljä avainsanaa. Eniten oikein arvannut joukkue voittaa, muutoin kyseessä on tasapeli. == Vihjeisiin liittyvät säännöt == * '''Saat valita vihjeiden muodon,''' joten vihje voi olla yksi sana tai kokonainen virke. * Vihjeiden täytyy perustua '''yleisesti saatavilla oleviin tietoihin''', kuten suomalaisiin runoilijoihin. Et voi siten '''koskaan''' viitata yksityis- tai sisäpiiritietoihin. * Vihjeiden täytyy viitata avainsanojen '''merkitykseen'''. * Vihjeet eivät koskaan saa viitata avainsanan '''kirjoitusasuun''', '''sijaintiin''' näyttöalustalla tai '''ääntämiseen'''. Pyydettäessä '''sinun on selitettävä vihjeesi ymmärrettävästi'''. * KOSKAAN EI saa käyttää samaa vihjettä '''useammin kuin kerran samassa pelissä'''. * '''KOSKAAN EI saa lukea ääneen koodikortin koodia tai käyttää avainsanoja''' (tai niiden käännöksiä toisella kielellä) '''vihjeinä''', ei edes eri avainsanan kohdalla. 1db9eaa9f31d34c2180a284e97cacdf880444220 1264 1263 2023-07-21T09:27:10Z Rexroom 5688 Tarkennuksia wikitext text/x-wiki Joukkueet pyrkivät viestimään omilleen numerosarjoja ilman että vastustajan joukkue saa ne siepattua. == Suomenkieliset avainsanat == Pelissä on myös avainsanat suomeksi, mutta niiden käyttöönotto vaatii valikkonavigointia: '''Pelaa nyt > Manuaalinen > Decrypto + Pelaa nyt! > Luo > Game Language > Suomi''' Voit myös asettaa Pelaa nyt -sivun pelikohtaisista asetuksista kielipreferenssiksi suomen. Helpointa olisi kuitenkin järjestää etukäteen peli kavereiden kanssa. == Pelin kulku == Pelin tavoitteena on kerätä joukkueellesi 2 sieppaus-merkkiä arvaamalla oikein vastajoukkueen koodikortin numerosarjan. Jos toisaalta joukkueesi kerää 2 väärä tulkinta -merkkiä, häviätte. === Kierroksen kulku === Kummastakin joukkueesta valitaan yksi pelaajista salakoodaajaksi. Salakoodaajat nostavat oman koodikortin. Salakoodaajat kirjoittavat 3 vinkkiä. '''Valkoisen joukkueen salakoodajan vinkit paljastetaan.''' '''Kummatkin joukkueet miettivät valkoisen joukkueen salakoodaajan vinkkejä keskenään ja kirjoittavat ylös mahdollisen numerosarjan (valkoinen salakoodaaja seuraa vain sivusta).''' '''Valkoinen koodikortti paljastetaan. Jos musta joukkue on oikeassa, he saavat sieppausmerkin. Jos valkoinen joukkue on väärässä, he saavat Väärä tulkinta -merkin. Muutoin mitään muuta ei tapahdu.''' Toistakaa korostetut kohdat uudestaan, mutta nyt värit vaihdettuina. (Eli valkoiset mustina ja mustat valkoisina.) '''Huom!''' Ensimmäisessä kierroksessa ei ole sieppausyrityksiä. === Kierroksen päättyminen === Kierroksen lopussa tarkistetaan tilanne: * Jos yhdellä joukkueella on kaksi sieppausmerkkiä, kyseinen joukkue voittaa pelin. * Jos yhdellä joukkueella on kaksi väärä tulkinta -merkkiä, kyseinen joukkue häviää pelin. Muussa tapauksessa alkaa uusi kierros. === Tasapelin ratkaisu === Jos kummallakin joukkueella on kaksi sieppausmerkkiä tai yhdellä joukkueella on kaksi sieppaus- ja väärä tulkinta -merkkiä, pelimerkit muutetaan pisteiksi. Sieppausmerkit antavat +1, väärä tulkinta -merkit -1. Eniten pisteitä saanut joukkue voittaa. Jos tasatilanne jatkuu, joukkueet yrittävät arvata vastajoukkueen neljä avainsanaa. Eniten oikein arvannut joukkue voittaa, muutoin kyseessä on tasapeli. == Vihjeisiin liittyvät säännöt == * '''Saat valita vihjeiden muodon,''' joten vihje voi olla yksi sana tai kokonainen virke. * Vihjeiden täytyy perustua '''yleisesti saatavilla oleviin tietoihin''', kuten suomalaisiin runoilijoihin. Et voi siten '''koskaan''' viitata yksityis- tai sisäpiiritietoihin. * Vihjeiden täytyy viitata avainsanojen '''merkitykseen'''. * Vihjeet eivät koskaan saa viitata avainsanan '''kirjoitusasuun''', '''sijaintiin''' näyttöalustalla tai '''ääntämiseen'''. Pyydettäessä '''sinun on selitettävä vihjeesi ymmärrettävästi'''. * KOSKAAN EI saa käyttää samaa vihjettä '''useammin kuin kerran samassa pelissä'''. * '''KOSKAAN EI saa lukea ääneen koodikortin koodia tai käyttää avainsanoja''' (tai niiden käännöksiä toisella kielellä) '''vihjeinä''', ei edes eri avainsanan kohdalla. ab0ce108bd38c83cf9ff48e86438f0d85e1e04a9 1265 1264 2023-08-22T13:39:07Z Rexroom 5688 wikitext text/x-wiki Joukkueet pyrkivät viestimään omilleen numerosarjoja ilman että vastustajan joukkue saa ne siepattua. == Suomenkieliset avainsanat == Pelissä on myös avainsanat suomeksi, valitset vain pöydän asetuksissa pelin kieleksi Suomi. Luultavasti nopeammin löydät pelin suomeksi, jos järjestät etukäteen pelin kavereiden kanssa. == Pelin kulku == Pelin tavoitteena on kerätä joukkueellesi 2 sieppaus-merkkiä arvaamalla oikein vastajoukkueen koodikortin numerosarjan. Jos toisaalta joukkueesi kerää 2 väärä tulkinta -merkkiä, häviätte. === Kierroksen kulku === Kummastakin joukkueesta valitaan yksi pelaajista salakoodaajaksi. Salakoodaajat nostavat oman koodikortin. Salakoodaajat kirjoittavat 3 vinkkiä. '''Valkoisen joukkueen salakoodajan vinkit paljastetaan.''' '''Kummatkin joukkueet miettivät valkoisen joukkueen salakoodaajan vinkkejä keskenään ja kirjoittavat ylös mahdollisen numerosarjan (valkoinen salakoodaaja seuraa vain sivusta).''' '''Valkoinen koodikortti paljastetaan. Jos musta joukkue on oikeassa, he saavat sieppausmerkin. Jos valkoinen joukkue on väärässä, he saavat Väärä tulkinta -merkin. Muutoin mitään muuta ei tapahdu.''' Toistakaa korostetut kohdat uudestaan, mutta nyt värit vaihdettuina. (Eli valkoiset mustina ja mustat valkoisina.) '''Huom!''' Ensimmäisessä kierroksessa ei ole sieppausyrityksiä. === Kierroksen päättyminen === Kierroksen lopussa tarkistetaan tilanne: * Jos yhdellä joukkueella on kaksi sieppausmerkkiä, kyseinen joukkue voittaa pelin. * Jos yhdellä joukkueella on kaksi väärä tulkinta -merkkiä, kyseinen joukkue häviää pelin. Muussa tapauksessa alkaa uusi kierros. === Tasapelin ratkaisu === Jos kummallakin joukkueella on kaksi sieppausmerkkiä tai yhdellä joukkueella on kaksi sieppaus- ja väärä tulkinta -merkkiä, pelimerkit muutetaan pisteiksi. Sieppausmerkit antavat +1, väärä tulkinta -merkit -1. Eniten pisteitä saanut joukkue voittaa. Jos tasatilanne jatkuu, joukkueet yrittävät arvata vastajoukkueen neljä avainsanaa. Eniten oikein arvannut joukkue voittaa, muutoin kyseessä on tasapeli. == Vihjeisiin liittyvät säännöt == * '''Saat valita vihjeiden muodon,''' joten vihje voi olla yksi sana tai kokonainen virke. * Vihjeiden täytyy perustua '''yleisesti saatavilla oleviin tietoihin''', kuten suomalaisiin runoilijoihin. Et voi siten '''koskaan''' viitata yksityis- tai sisäpiiritietoihin. * Vihjeiden täytyy viitata avainsanojen '''merkitykseen'''. * Vihjeet eivät koskaan saa viitata avainsanan '''kirjoitusasuun''', '''sijaintiin''' näyttöalustalla tai '''ääntämiseen'''. Pyydettäessä '''sinun on selitettävä vihjeesi ymmärrettävästi'''. * KOSKAAN EI saa käyttää samaa vihjettä '''useammin kuin kerran samassa pelissä'''. * '''KOSKAAN EI saa lukea ääneen koodikortin koodia tai käyttää avainsanoja''' (tai niiden käännöksiä toisella kielellä) '''vihjeinä''', ei edes eri avainsanan kohdalla. 0c6f72d94655ec2807245d15d49ba519aece8703 Tips welcometo 0 212 1266 2023-11-03T22:52:23Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Korkeintaan 6 talon yhtiöt ovat pisteytettävissä, joten on hyvä käyttää maanmittaajaa rajojen rakentamisessa! Tietyn numeroisia kortteja on rajoitettu määrä. Nyrkkisääntönä pienimpiä ja suurimpia numeroita on vähän, ja keskivälillä olevia numeroita on eniten. Korttien lukumäärät: * 1: 3 kpl * 2: 3 kpl * 3: 4 kpl * 4: 5 kpl * 5: 6 kpl * 6: 7 kpl * 7: 8 kpl * 8: 9 kpl * 9: 8 kpl * 10: 7 kpl * 11: 6 kpl * 12: 5 kpl * 13: 4 kpl * 14: 3 kpl * 15: 3 kpl cfb7039b1522b4e5cbe9947925f00d0218a67597 Gamehelpbeyondthesun 0 205 1267 1252 2024-01-28T10:43:09Z Rexroom 5688 Uusi osio wikitext text/x-wiki Avaruussivilisaatiopeli 2–4 pelaajalle, jossa tehdään avaruustutkimusta ja käydään läpi "teknologiapuuta". == Ikonit == [SHIP]/[SUPPLY]/[POPULATION]: Pelaajakuutioita. Sinun [SUPPLY] odottavat pelaajalaudallasi ennen muuttamista [SHIP] tai [POPULATION]. [SUPPLY][ARROW][POPULATION]: Muunna yksi korikuutio populaatiokuutioksi. [ORE]: Malmia. Näet nykyisen malmivarantosi pelaajapaneelistasi. [LOSE_POPULATION]: Menetä populaatiokuutio (maksuna jostain toiminnosta). [I]/[II]/[III]/[IIII]: Tason 1/2/3/4-teknologia. [RESEARCH][TECHX]: Tutki teknologia. Vaatii [POPULATION]. [VP]: Voittopisteitä. [COLONIZE]: Kolonisoi planeettakunta. Sinun on hallittava se ja pitää siellä vaaditun määrän alusvoimaa. == Teknologia-alat == Pelissä on 4 erinäistä teknologia-alaa, joilla on omat erityispiirteensä. Jotkut tutkittavat teknologiat ovat kahden alan hybridejä. * Kaupankäynti – kolonisointitoiminnot, liikkumiset * Sotatekniikka – alusrakennus, liikkumiset, parantelut * Talous – ruoantuotantoautomatisointi, väestönkasvu, malmintuotanto * Tiede – malmintuotantoautomatisointi, tutkimustoiminnot, malmibonukset == Pelin kulku == Pelaajan vuorossa on kolme vaihetta: 1. Toimintavaihe: aseta työläisesi vapaaseen toimintoheksaan ja maksa mahdolliset kulut. [I]/[II]/[III]/[IIII]-korteissa olevat toiminnot vaativat, että kyseistä teknologiaa on tutkittu pelaajan toimesta. 2. Tuotantovaihe: voit joko a) [POPULATION] kasvattaa väestöä, b) [ORE] tuottaa malmia tai c) tehdä resurssivaihtoa. Väestönkasvussa jokaisesta sarakkeesta, jonka alla ei ole ruoantuotantolevy, muuttuu 1 korikuutio populaatioksi. (Huom. Jos sarakkeessa ei ole kuutioita, et saa sieltä populaatiota.) Malmintuotannossa jokainen paljas malmisymboli malmintuotantoradalla antaa sinulle yhden malmin. 3. Saavutusvaihe: tarkista, onko pelaaja saavuttanut jonkun saavutuksista ja valitsee sitten '''yhden''' vuoronsa aikana == Toiminnot ja kortit == Pelaajilla on aina käytössä päälaudan sivussa olevat avaruuslennon toiminnot ja mahdolliset paljastetut killan toiminnot, kunhan on toimintoheksa, jonne voi siirtää. Avaruuslennon 4. toiminto on aina käytettävissä. Kun tutkit teknologiakortteja, huomaa, että korteissa on usein kaksi osaa: välittömät vaikutukset ja uudet toiminnot/passiiviset vaikutukset. Välittömät vaikutukset suoritetaan heti tutkimuksen jälkeen, eikä enää sen jälkeen. Uudet toimintoheksat tulevat käytettäväksi seuraavalla vuorollasi, mutta passiiviset vaikutukset vaikuttavat siitä eteenpäin aina. == Pelin päättyminen == Kun 2–3-pelaajan pelissä on otettu 3 saavutusta tai 4-pelaajan pelissä 4 saavutusta jonkun pelaajan vuoron lopussa, kierros pelataan normaalisti loppuun, jonka jälkeen pelataan vielä viimeinen kierros alkaen aloituspelaajasta. Tämän jälkeen alkaa loppupisteytys. === Loppupisteytys === Pelin lopussa suoritetaan loppupisteytys. Valtaosa pisteistä lasketaan jo pelin aikana. {| class="wikitable" style="text-align: center" !Kohde !Voittopisteitä |- |[I] |1 per teknologia |- |[II] |2 per teknologia |- |[III] |3 per teknologia |- |Yksityiset teknologiat |2–3 riippuen tasosta |- |[IIII] |määritelty kortissa |- |Automaatiorata |pisteitä peitetyistä koloista |- |Kolosinoidut planeettakunnat |merkitty kolonisoituun korttiin |- |Hallitut planeettakunnat<br>(myös telakkakunnat) |1 per kohde |- |Ei-hallittavat kohteet<br>(Sol ja ulkoavaruus) |1 per kohde<br>(sotavoiman enemmistö, tasapeleissä jaettu) |- |Tapahtumakortit |merkitty tapahtumakortteihin |- |Saavutukset |merkitty saavutuksiin |} Tasatilanteissa vähiten [FOOD_DISC]/[ORE_DISC] pelaajalaudalla pitävä pelaaja voittaa. Toinen ratkaisija on eniten [POPULATION] ja viimeisenä eniten [ORE]. Jos tasatilanne pysyy samana, niin pelaajat päätyvät tasapeliin. ad7d6815793e416a69b5ae41cac647f0c9b2de04 Gamehelproomtwentyfive 0 213 1268 2024-03-13T07:54:15Z LaNinja 6044 Page draft created wikitext text/x-wiki == TAVOITTEET == In COOPERATION mode, players control prisoners trying to find Room 25 and escape together before the end of the countdown. In SUSPICION mode, one to two guards infiltrate the prisoners, looking to prevent their escape through deception or brutality! VERY IMPORTANT: '''Players always start in the central room, and the Room 25 is always initially placed amongst the 12 tiles at the corners (3 in each corner).''' == COUNTDOWN == Depending on the game mode and the number of players, the game lasts between 6 and 10 turns. In SUSPICION mode, there are 10 turns for 4-6 characters, 9 turns for 7 characters and 8 for 8 characters. In COOPERATION mode, there are 8 turns for 4 characters, 7 turns for 5 characters and 6 for 6 characters. During each turn, characters move first from left to right, as indicated on the countdown board. At the end of each turn, first character becomes the last one, and is turn marker is moved to the right of the previous last character on the countdown board. == PELIASETELMA == === SUSPICION MODE (4 OR 5 PLAYERS) === BLUE: Central room + Room 25 GREEN: 5 Random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 2 Mortal rooms + 1 Timer room + 7 random tiles === SUSPICION MODE (6 PLAYERS) === BLUE: Central room + Room 25 GREEN: 1 Regeneration room + 1 Robot room(*) + 5 random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 1 Mortal rooms + 2 Shredder rooms + 7 random tiles (*) Warning! During the setup the Robot rooms are part of the 12 rooms that is placed on the Exit Zones, as the Vision Room. == PELIN KULKU == Each turn includes 3 different phases: •PROGRAMMING: All players program simultaneously up to 2 actions by turn and, if they want, the ADRENALINE only once in the game •ACTION: In turn, the players resolve their first action, then the second •COUNTDOWN (advance the turn marker and change the turn order) All the players have a starting clue. During the first Turn, before the first Programming phase, each player secretly looks at one of the four rooms adjacent to the Central room, and replaces it face-down in its initial location. Warning: Each player looks at ONE room only, even if they play more than one characters. === OHJELMOINTI === Each character has 4 base actions (Look, Move, Push and Control) plus a Special Talent (if enabled). Each player chooses two actions. The order of the actions is important, the one placed first will be played first. Players choose their actions secretly and simultaneously. A player can also choose to program only one action. During the Action phase they will be able to choose if they play it during the first action turn or during the second one. Adrenaline token: The Adrenaline token allows the character to take a third action, once per game. During the programming phase, a player must decide whether or not to use its Adrenaline token. === ACTION === The first player reveals their first action and resolves it immediately. Then, following the turn order, the next player does the same and so on until all the players resolved their first actions. Second actions are resolved using the same process. Every programmed action MUST be executed, even if it works against the player. If one action cannot be completed, the action is lost. === LOOK === Choose and observe secretly a room adjacent to your character. Once you have looked at it, replace it face down. You are allowed to give indications about the room’s danger level (the color of the room) but you CANNOT tell the other players the name of the room. === MOVE === Move your character to an adjacent room. If the room is still hidden, it is revealed when you reach it. The room will stay revealed for the rest of the game. A room effect applies every time a character enters it. === PUSH === Move a character standing in the same room as you to an adjacent room. Your character stays put and the pushed character immediately applies the effects of the room in which they land. If the room was still hidden it’s revealed. Characters can't use Push inside the Central Room. === CONTROL === Move the line that includes your character’s room one rank in the direction you choose (vertically or horizontally). All the rooms slide one rank in the same direction, all the characters stay on the rooms they’re standing on and slide with them. The room at the end of the line exits the Complex and is replaced at the other end of the line. Afterwards, a Control token is placed next to the line you just moved with the arrow pointing in the direction of the movement. 1st exception: the Central room CANNOT be moved and always stays at the Complex’s center. Therefore, it is IMPOSSIBLE to use the Control action to move a central line (containing the Central Room). 2nd exception: A line can be moved several times during the same turn but ONLY in the SAME direction. === SPECIAL TALENT (IF ENABLED) === This special ability is represented by a special token for each character, which is added to their four basic actions (Look, Move, Push, and Control). During their programming phase, players now select two tokens from five available. Important : Special Abilities CANNOT be used in the Central Room NOR using Adrenaline. Issues (since it seems the Dev abandoned the game): Emmett - Lock On: -the ability description is wrong: you cannot place a lock on a room you occupy, the bga adaptation functions correctly. -the bga adaptation is not allowing you to place a lock on an adjacent room while you are in the central room, which should be allowed under the rules. === ADRENALINE (IF ENABLED) === After the Action step, if at least one player has scheduled an Adrenaline action, one more round is played. In turn order, each player who scheduled an Adrenaline action carries out one of the four basic actions (Look, Move, Push, or Control) immediately. They can even repeat an action already taken this turn or an action lost due to a penalty. The adrenaline token is then lost. === M.A.C. CARDS (IF ENABLED, COOPERATION AND SOLO MODES ONLY) === The M.A.C. cards (MOVE ALONE COMPLEX) generate immediate events that affect the complex or the characters and make the game much more difficult (but might provide assistance). M.A.C. cards have two levels of difficulty: * Barrier cards - complex movement cards, Punishment cards, and various others. * Madness cards (only in “Madness Level” Solo and Cooperative play) - more difficult and varied. Note: Players can look through the M.A.C. card discards at any point during the game. === REMINDER TOKENS === Each player has a Reminder Token they can use at any time during their turn, in addition to their actions. They can place the token on any room still hidden anywhere on the board as a reminder. The token is recovered only when the room it’s placed on is revealed. Once recovered, the player can use it again. To place a reminder token double click on the room during the look action. NOTE: You are unable to put down a reminder token during the first look pre-programming action when the game starts. == COUNTDOWN == When all actions have been resolved, the TURN ENDS: All Control tokens are removed from the complex. The first player becomes the last player. NOTE: Adrenaline actions are not within the same turn. The turn ends and then the adrenaline actions are resolved. This is important for rooms like flooded - where you have to move out of the room before the end of your next action. == WINNING CONDITIONS == === PRISONERS === To escape the Complex the characters have to: # Activate the Key Room (Cooperation Mode only) # Find the Room 25 and made each prisoners reach it # Move ROOM 25 from EXIT zone towards outside the complex with the CONTROL action, once all prisoners are inside. It is IMPOSSIBLE to escape alone. In Suspicion Mode, the prisoners can escape even if 1 or 2 guards remain within the ROOM 25. Also, if only one prisoner has been killed or didn’t reach Room 25 in time, the others can still escape but ONLY during the LAST TURN of the countdown. In SOLO mode, all prisoners should escape; the game is lost even if one prisoner dies. === GUARDS === The guards win if two prisoners are killed or if the prisoners did not escape before the end of the countdown. == IDENTITY REVEALS == === TAKE OFF THE MASKS === When playing with a single guard, a character must reveal their identity whenever all the others characters have reached Room 25 (even if that character is a prisoner). When playing with two guards, the same applies as soon as two characters are left out of Room 25. In both cases, the identities of players inside Room 25 are kept secret. === AFTER A CHARACTER ELIMINATION === If one character is killed, they keep their identity secret. If a second character is killed: The 1st killed MUST reveal their identity. If the 1st character is a guard, the game continues. If the 1st character is a prisoner, the 2nd character killed MUST reveal their identity. If the 2nd is a guard, the game continues. If the 2nd is also a prisoner, the game ends and the guards win. 00022bd603af908df468f52df084c5976b2767b3 1269 1268 2024-03-13T08:01:49Z LaNinja 6044 small edits wikitext text/x-wiki == TAVOITTEET == In COOPERATION mode, players control prisoners trying to find Room 25 and escape together before the end of the countdown. In SUSPICION mode, one to two guards infiltrate the prisoners, looking to prevent their escape through deception or brutality! VERY IMPORTANT: '''Players always start in the central room, and the Room 25 is always initially placed amongst the 12 tiles at the corners (3 in each corner).''' == COUNTDOWN == Depending on the game mode and the number of players, the game lasts between 6 and 10 turns. In SUSPICION mode, there are 10 turns for 4-6 characters, 9 turns for 7 characters and 8 for 8 characters. In COOPERATION mode, there are 8 turns for 4 characters, 7 turns for 5 characters and 6 for 6 characters. During each turn, characters move first from left to right, as indicated on the countdown board. At the end of each turn, first character becomes the last one, and is turn marker is moved to the right of the previous last character on the countdown board. == PELIASETELMA == === SUSPICION MODE (4 OR 5 PLAYERS) === BLUE: Central room + Room 25 GREEN: 5 Random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 2 Mortal rooms + 1 Timer room + 7 random tiles === SUSPICION MODE (6 PLAYERS) === BLUE: Central room + Room 25 GREEN: 1 Regeneration room + 1 Robot room(*) + 5 random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 1 Mortal rooms + 2 Shredder rooms + 7 random tiles (*) Warning! During the setup the Robot rooms are part of the 12 rooms that is placed on the Exit Zones, as the Vision Room. == PELIN KULKU == Each turn includes 3 different phases: •PROGRAMMING: All players program simultaneously up to 2 actions by turn and, if they want, the ADRENALINE only once in the game •ACTION: In turn, the players resolve their first action, then the second •COUNTDOWN (advance the turn marker and change the turn order) All the players have a starting clue. During the first Turn, before the first Programming phase, each player secretly looks at one of the four rooms adjacent to the Central room, and replaces it face-down in its initial location. Warning: Each player looks at ONE room only, even if they play more than one characters. === OHJELMOINTI === Each character has 4 base actions (Look, Move, Push and Control) plus a Special Talent (if enabled). Each player chooses two actions. The order of the actions is important, the one placed first will be played first. Players choose their actions secretly and simultaneously. A player can also choose to program only one action. During the Action phase they will be able to choose if they play it during the first action turn or during the second one. Adrenaline token: The Adrenaline token allows the character to take a third action, once per game. During the programming phase, a player must decide whether or not to use its Adrenaline token. === ACTION === The first player reveals their first action and resolves it immediately. Then, following the turn order, the next player does the same and so on until all the players resolved their first actions. Second actions are resolved using the same process. Every programmed action MUST be executed, even if it works against the player. If one action cannot be completed, the action is lost. === LOOK === Choose and observe secretly a room adjacent to your character. Once you have looked at it, replace it face down. You are allowed to give indications about the room’s danger level (the color of the room) but you CANNOT tell the other players the name of the room. === MOVE === Move your character to an adjacent room. If the room is still hidden, it is revealed when you reach it. The room will stay revealed for the rest of the game. A room effect applies every time a character enters it. === PUSH === Move a character standing in the same room as you to an adjacent room. Your character stays put and the pushed character immediately applies the effects of the room in which they land. If the room was still hidden it’s revealed. Characters can't use Push inside the Central Room. === CONTROL === Move the line that includes your character’s room one rank in the direction you choose (vertically or horizontally). All the rooms slide one rank in the same direction, all the characters stay on the rooms they’re standing on and slide with them. The room at the end of the line exits the Complex and is replaced at the other end of the line. Afterwards, a Control token is placed next to the line you just moved with the arrow pointing in the direction of the movement. 1st exception: the Central room CANNOT be moved and always stays at the Complex’s center. Therefore, it is IMPOSSIBLE to use the Control action to move a central line (containing the Central Room). 2nd exception: A line can be moved several times during the same turn but ONLY in the SAME direction. === SPECIAL TALENT (IF ENABLED) === This special ability is represented by a special token for each character, which is added to their four basic actions (Look, Move, Push, and Control). During their programming phase, players now select two tokens from five available. Important : Special Abilities CANNOT be used in the Central Room NOR using Adrenaline. Issues (since it seems the Dev abandoned the game): Emmett - Lock On: -the ability description is wrong: you cannot place a lock on a room you occupy, the bga adaptation functions correctly. -the bga adaptation is not allowing you to place a lock on an adjacent room while you are in the central room, which should be allowed under the rules. === ADRENALINE (IF ENABLED) === After the Action step, if at least one player has scheduled an Adrenaline action, one more round is played. In turn order, each player who scheduled an Adrenaline action carries out one of the four basic actions (Look, Move, Push, or Control) immediately. They can even repeat an action already taken this turn or an action lost due to a penalty. The adrenaline token is then lost. === M.A.C. CARDS (IF ENABLED, COOPERATION AND SOLO MODES ONLY) === The M.A.C. cards (MOVE ALONE COMPLEX) generate immediate events that affect the complex or the characters and make the game much more difficult (but might provide assistance). M.A.C. cards have two levels of difficulty: * Barrier cards - complex movement cards, Punishment cards, and various others. * Madness cards (only in “Madness Level” Solo and Cooperative play) - more difficult and varied. Note: Players can look through the M.A.C. card discards at any point during the game. === REMINDER TOKENS === Each player has a Reminder Token they can use at any time during their turn, in addition to their actions. They can place the token on any room still hidden anywhere on the board as a reminder. The token is recovered only when the room it’s placed on is revealed. Once recovered, the player can use it again. To place a reminder token double click on the room during the look action. NOTE: You are unable to put down a reminder token during the first look pre-programming action when the game starts. == COUNTDOWN == When all actions have been resolved, the TURN ENDS: All Control tokens are removed from the complex. The first player becomes the last player. NOTE: Adrenaline actions are not within the same turn. The turn ends and then the adrenaline actions are resolved. This is important for rooms like flooded - where you have to move out of the room before the end of your next action. == VOITTOEHDOT == === PRISONERS === To escape the Complex the characters have to: # Activate the Key Room (Cooperation Mode only) # Find the Room 25 and made each prisoners reach it # Move ROOM 25 from EXIT zone towards outside the complex with the CONTROL action, once all prisoners are inside. It is IMPOSSIBLE to escape alone. In Suspicion Mode, the prisoners can escape even if 1 or 2 guards remain within the ROOM 25. Also, if only one prisoner has been killed or didn’t reach Room 25 in time, the others can still escape but ONLY during the LAST TURN of the countdown. In SOLO mode, all prisoners should escape; the game is lost even if one prisoner dies. === GUARDS === The guards win if two prisoners are killed or if the prisoners did not escape before the end of the countdown. == ROOLIEN PALJASTAMINEN == === TAKE OFF THE MASKS === When playing with a single guard, a character must reveal their identity whenever all the others characters have reached Room 25 (even if that character is a prisoner). When playing with two guards, the same applies as soon as two characters are left out of Room 25. In both cases, the identities of players inside Room 25 are kept secret. === AFTER A CHARACTER ELIMINATION === If one character is killed, they keep their identity secret. If a second character is killed: The 1st killed MUST reveal their identity. If the 1st character is a guard, the game continues. If the 1st character is a prisoner, the 2nd character killed MUST reveal their identity. If the 2nd is a guard, the game continues. If the 2nd is also a prisoner, the game ends and the guards win. 10c54fb0b933fa84588a5893a4590bd5d55c4eca 1270 1269 2024-03-13T13:18:31Z LaNinja 6044 small edits wikitext text/x-wiki == TAVOITTEET == In COOPERATION mode, players control prisoners trying to find Room 25 and escape together before the end of the countdown. In SUSPICION mode, one to two guards infiltrate the prisoners, looking to prevent their escape through deception or brutality! VERY IMPORTANT: '''Players always start in the central room, and the Room 25 is always initially placed amongst the 12 tiles at the corners (3 in each corner).''' == KIERROSTEN MÄÄRÄ == Depending on the game mode and the number of players, the game lasts between 6 and 10 turns. In SUSPICION mode, there are 10 turns for 4-6 characters, 9 turns for 7 characters and 8 for 8 characters. In COOPERATION mode, there are 8 turns for 4 characters, 7 turns for 5 characters and 6 for 6 characters. During each turn, characters move first from left to right, as indicated on the countdown board. At the end of each turn, first character becomes the last one, and is turn marker is moved to the right of the previous last character on the countdown board. == PELIASETELMA == === SUSPICION MODE (4 OR 5 PLAYERS) === BLUE: Central room + Room 25 GREEN: 5 Random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 2 Mortal rooms + 1 Timer room + 7 random tiles === SUSPICION MODE (6 PLAYERS) === BLUE: Central room + Room 25 GREEN: 1 Regeneration room + 1 Robot room(*) + 5 random tiles YELLOW: 1 Jamming room + 7 random tiles RED: 1 Mortal rooms + 2 Shredder rooms + 7 random tiles (*) Warning! During the setup the Robot rooms are part of the 12 rooms that is placed on the Exit Zones, as the Vision Room. == PELIN KULKU == Each turn includes 3 different phases: •PROGRAMMING: All players program simultaneously up to 2 actions by turn and, if they want, the ADRENALINE only once in the game •ACTION: In turn, the players resolve their first action, then the second •COUNTDOWN (advance the turn marker and change the turn order) All the players have a starting clue. During the first Turn, before the first Programming phase, each player secretly looks at one of the four rooms adjacent to the Central room, and replaces it face-down in its initial location. Warning: Each player looks at ONE room only, even if they play more than one characters. === OHJELMOINTI === Each character has 4 base actions (Look, Move, Push and Control) plus a Special Talent (if enabled). Each player chooses two actions. The order of the actions is important, the one placed first will be played first. Players choose their actions secretly and simultaneously. A player can also choose to program only one action. During the Action phase they will be able to choose if they play it during the first action turn or during the second one. Adrenaline token: The Adrenaline token allows the character to take a third action, once per game. During the programming phase, a player must decide whether or not to use its Adrenaline token. === ACTION === The first player reveals their first action and resolves it immediately. Then, following the turn order, the next player does the same and so on until all the players resolved their first actions. Second actions are resolved using the same process. Every programmed action MUST be executed, even if it works against the player. If one action cannot be completed, the action is lost. === KATSO === Choose and observe secretly a room adjacent to your character. Once you have looked at it, replace it face down. You are allowed to give indications about the room’s danger level (the color of the room) but you CANNOT tell the other players the name of the room. === LIIKU === Move your character to an adjacent room. If the room is still hidden, it is revealed when you reach it. The room will stay revealed for the rest of the game. A room effect applies every time a character enters it. === TÖNÄISE === Move a character standing in the same room as you to an adjacent room. Your character stays put and the pushed character immediately applies the effects of the room in which they land. If the room was still hidden it’s revealed. Characters can't use Push inside the Central Room. === OHJAA === Move the line that includes your character’s room one rank in the direction you choose (vertically or horizontally). All the rooms slide one rank in the same direction, all the characters stay on the rooms they’re standing on and slide with them. The room at the end of the line exits the Complex and is replaced at the other end of the line. Afterwards, a Control token is placed next to the line you just moved with the arrow pointing in the direction of the movement. 1st exception: the Central room CANNOT be moved and always stays at the Complex’s center. Therefore, it is IMPOSSIBLE to use the Control action to move a central line (containing the Central Room). 2nd exception: A line can be moved several times during the same turn but ONLY in the SAME direction. === ERIKOISKYKY (JOS KÄYTÖSSÄ) === This special ability is represented by a special token for each character, which is added to their four basic actions (Look, Move, Push, and Control). During their programming phase, players now select two tokens from five available. Important : Special Abilities CANNOT be used in the Central Room NOR using Adrenaline. Issues (since it seems the Dev abandoned the game): Emmett - Lock On: -the ability description is wrong: you cannot place a lock on a room you occupy, the bga adaptation functions correctly. -the bga adaptation is not allowing you to place a lock on an adjacent room while you are in the central room, which should be allowed under the rules. === ADRENALINE (IF ENABLED) === After the Action step, if at least one player has scheduled an Adrenaline action, one more round is played. In turn order, each player who scheduled an Adrenaline action carries out one of the four basic actions (Look, Move, Push, or Control) immediately. They can even repeat an action already taken this turn or an action lost due to a penalty. The adrenaline token is then lost. === M.A.C. CARDS (IF ENABLED, COOPERATION AND SOLO MODES ONLY) === The M.A.C. cards (MOVE ALONE COMPLEX) generate immediate events that affect the complex or the characters and make the game much more difficult (but might provide assistance). M.A.C. cards have two levels of difficulty: * Barrier cards - complex movement cards, Punishment cards, and various others. * Madness cards (only in “Madness Level” Solo and Cooperative play) - more difficult and varied. Note: Players can look through the M.A.C. card discards at any point during the game. === REMINDER TOKENS === Each player has a Reminder Token they can use at any time during their turn, in addition to their actions. They can place the token on any room still hidden anywhere on the board as a reminder. The token is recovered only when the room it’s placed on is revealed. Once recovered, the player can use it again. To place a reminder token double click on the room during the look action. NOTE: You are unable to put down a reminder token during the first look pre-programming action when the game starts. == COUNTDOWN == When all actions have been resolved, the TURN ENDS: All Control tokens are removed from the complex. The first player becomes the last player. NOTE: Adrenaline actions are not within the same turn. The turn ends and then the adrenaline actions are resolved. This is important for rooms like flooded - where you have to move out of the room before the end of your next action. == VOITTOEHDOT == === VANGIT === To escape the Complex the characters have to: # Activate the Key Room (Cooperation Mode only) # Find the Room 25 and made each prisoners reach it # Move ROOM 25 from EXIT zone towards outside the complex with the CONTROL action, once all prisoners are inside. It is IMPOSSIBLE to escape alone. In Suspicion Mode, the prisoners can escape even if 1 or 2 guards remain within the ROOM 25. Also, if only one prisoner has been killed or didn’t reach Room 25 in time, the others can still escape but ONLY during the LAST TURN of the countdown. In SOLO mode, all prisoners should escape; the game is lost even if one prisoner dies. === VARTIJAT === The guards win if two prisoners are killed or if the prisoners did not escape before the end of the countdown. == ROOLIEN PALJASTAMINEN == === TAKE OFF THE MASKS === When playing with a single guard, a character must reveal their identity whenever all the others characters have reached Room 25 (even if that character is a prisoner). When playing with two guards, the same applies as soon as two characters are left out of Room 25. In both cases, the identities of players inside Room 25 are kept secret. === AFTER A CHARACTER ELIMINATION === If one character is killed, they keep their identity secret. If a second character is killed: The 1st killed MUST reveal their identity. If the 1st character is a guard, the game continues. If the 1st character is a prisoner, the 2nd character killed MUST reveal their identity. If the 2nd is a guard, the game continues. If the 2nd is also a prisoner, the game ends and the guards win. a0a2f1bde455292780a1a2a8aebfca043afb4bf3 Gamehelpbarenpark 0 214 1271 2024-04-11T06:18:43Z Silkkii 5977 Ak: Uusi sivu: == Vuoron pelaaminen == Aloita asettamalla ensimmäinen laatta puistoalueellesi. Sen tulee mahtua alueelle, eikä se saa peittää kuoppaa. Seuraavina vuoroina aseta mikä tahansa saamasi laatta puistoalueellesi niin, että se koskettaa vähintään yhtä aiemmin asetetun laatan reunaa. Sen tulee silti mahtua laudalle, eikä se saa peittää kuoppaa. Jos sinulla ei ole asetettavaa laattaa, sinulla ei ole muuta vaihtoehtoa kuin ohittaa. Saat ohittamisen jälkeen ottaa yhden... wikitext text/x-wiki == Vuoron pelaaminen == Aloita asettamalla ensimmäinen laatta puistoalueellesi. Sen tulee mahtua alueelle, eikä se saa peittää kuoppaa. Seuraavina vuoroina aseta mikä tahansa saamasi laatta puistoalueellesi niin, että se koskettaa vähintään yhtä aiemmin asetetun laatan reunaa. Sen tulee silti mahtua laudalle, eikä se saa peittää kuoppaa. Jos sinulla ei ole asetettavaa laattaa, sinulla ei ole muuta vaihtoehtoa kuin ohittaa. Saat ohittamisen jälkeen ottaa yhden viheralueen ja vuorosi päättyy siihen. == Kuvakkeiden peittäminen == Kun mikä tahansa laatan osa peittää kuvakkeen puistolaudalla, saat tehdä kyseisen kuvakkeen toiminnon. {| class="wikitable" style="width:auto;text-align:left;" border="2" ! Icon ! Effect |- |<b style="color:green">Vihreä kottikärry</b>||Valitse itsellesi viheralueen laatta (1-3 ruutua). |- |<b style="color:grey">Valkoinen sementtitrekka</b>||Valitse itsellesi eläintalon laatta (4 ruutua) tai viheralueen laatta (1-3 ruutua) ja lisää se varastoosi. Puistoalueelle asetetut eläintalot tuottavat pisteitä. |- |<b style="color:orange">Oranssi kaivinkone</b>||Valitse itsellsei aitauksen laatta (5 ruutua) ja lisää se varastoosi. Puistoalueelle asetetut aitaukset tuottavat pisteitä. |- |Rakennustyöläiset||Valitse uusi puistoalue ja aseta se puistoosi välittömästi. Sen täytyy olla aiemman alueen vieressä ja se ei voi peittää puiston sisäänkäyntiä. |} Kun täytät minkä tahansa puistoalueen kokonaan, saat karhupatsaan josta saat voittopisteitä. Puistoalueen aikaisemmin valmiiksi saattaminen tuottaa enemmän voittopisteitä. == Tavoitteet == Useimmat kokeneet Bärenpark-pelaajat suosivat pelaamista tavoitteiden kanssa. Kolme satunnaista tavoitetta sijoitetaan yleisen varaston alle. Vie osoitin tavoitteen päälle (tai tee pitkä painallus mobiililaitteella) nähdäksesi, mitä niiden saavuttamiseksi tarvitaan. Nopea tavoitteiden suorittaminen tuottaa enemmän voittopisteitä. == Pelin päättyminen == Kun joku pelaajista saa kaikki neljä puistoaluettaan täytettyä, muut pelaajat saavat vielä yhden vuoron ja sen jälkeen peli päättyy. Peli pisteytetään ja pelaaja jolla on eniten pisteitä voittaa. Tasapelitilanteessa se pelaaja voittaa jonka varaston (asettamattomien) laattojen kokonaisarvo on suurin. Jos peli on vieläkin tasan, pelillä on useampi voittaja. == Kuoppamuunnos == Tässä kokeneen pelaajan muunnoksessa voit pelin viimeisellä vuorolla peittää kuopat tavallisilla laatoilla. Huomaa, että jos olet peittänyt kuopan, et saa siitä puistoalueesta karhubonusta. == Souvenir Shops -minilisäosa == Kun peität rakennustyöläisten kuvakkeen viimeisellä (eli neljännellä) puistoalueellasi, valitse välittömästi saatavilla oleva matkamuistomyymälälaatta ja aseta se varastoon. Mitä suuremman laatan valitset, sitä vähemmän pisteitä saat sen asettamisesta. 752d62ef778e3867c2573dde4564628d77871631 1272 1271 2024-04-11T06:20:25Z Silkkii 5977 wikitext text/x-wiki == Vuoron pelaaminen == Aloita asettamalla ensimmäinen laatta puistoalueellesi. Sen tulee mahtua alueelle, eikä se saa peittää kuoppaa. Seuraavina vuoroina aseta mikä tahansa saamasi laatta puistoalueellesi niin, että se koskettaa vähintään yhtä aiemmin asetetun laatan reunaa. Sen tulee silti mahtua laudalle, eikä se saa peittää kuoppaa. Jos sinulla ei ole asetettavaa laattaa, sinulla ei ole muuta vaihtoehtoa kuin ohittaa. Saat ohittamisen jälkeen ottaa yhden viheralueen ja vuorosi päättyy siihen. == Kuvakkeiden peittäminen == Kun mikä tahansa laatan osa peittää kuvakkeen puistolaudalla, saat tehdä kyseisen kuvakkeen toiminnon. {| class="wikitable" style="width:auto;text-align:left;" border="2" ! Icon ! Effect |- |<b style="color:green">Vihreät kottikärryt</b>||Valitse itsellesi viheralueen laatta (1-3 ruutua). |- |<b style="color:grey">Valkoinen sementtitrekka</b>||Valitse itsellesi eläintalon laatta (4 ruutua) tai viheralueen laatta (1-3 ruutua) ja lisää se varastoosi. Puistoalueelle asetetut eläintalot tuottavat pisteitä. |- |<b style="color:orange">Oranssi kaivinkone</b>||Valitse itsellsei aitauksen laatta (5 ruutua) ja lisää se varastoosi. Puistoalueelle asetetut aitaukset tuottavat pisteitä. |- |Rakennustyöläiset||Valitse uusi puistoalue ja aseta se puistoosi välittömästi. Sen täytyy olla aiemman alueen vieressä ja se ei voi peittää puiston sisäänkäyntiä. |} Kun täytät minkä tahansa puistoalueen kokonaan, saat karhupatsaan josta saat voittopisteitä. Puistoalueen aikaisemmin valmiiksi saattaminen tuottaa enemmän voittopisteitä. == Tavoitteet == Useimmat kokeneet Bärenpark-pelaajat suosivat pelaamista tavoitteiden kanssa. Kolme satunnaista tavoitetta sijoitetaan yleisen varaston alle. Vie osoitin tavoitteen päälle (tai tee pitkä painallus mobiililaitteella) nähdäksesi, mitä niiden saavuttamiseksi tarvitaan. Nopea tavoitteiden suorittaminen tuottaa enemmän voittopisteitä. == Pelin päättyminen == Kun joku pelaajista saa kaikki neljä puistoaluettaan täytettyä, muut pelaajat saavat vielä yhden vuoron ja sen jälkeen peli päättyy. Peli pisteytetään ja pelaaja jolla on eniten pisteitä voittaa. Tasapelitilanteessa se pelaaja voittaa jonka varaston (asettamattomien) laattojen kokonaisarvo on suurin. Jos peli on vieläkin tasan, pelillä on useampi voittaja. == Kuoppamuunnos == Tässä kokeneen pelaajan muunnoksessa voit pelin viimeisellä vuorolla peittää kuopat tavallisilla laatoilla. Huomaa, että jos olet peittänyt kuopan, et saa siitä puistoalueesta karhubonusta. == Souvenir Shops -minilisäosa == Kun peität rakennustyöläisten kuvakkeen viimeisellä (eli neljännellä) puistoalueellasi, valitse välittömästi saatavilla oleva matkamuistomyymälälaatta ja aseta se varastoon. Mitä suuremman laatan valitset, sitä vähemmän pisteitä saat sen asettamisesta. 5496e5137e6ad4635f62f630d256e505a693b924 Gamehelpitsawonderfulworld 0 215 1273 2024-08-19T22:26:34Z Aureon100 6061 Ak: Uusi sivu: You are leading an expanding Empire. You must choose the path that will get you to develop faster and better than your competitors. Johdat kuningaskunnan laajentamista. Sinun täytyy valita polku, joka saa sinut kehittymään nopeammaksi ja paremmaksi kuin kilpailijasi. wikitext text/x-wiki You are leading an expanding Empire. You must choose the path that will get you to develop faster and better than your competitors. Johdat kuningaskunnan laajentamista. Sinun täytyy valita polku, joka saa sinut kehittymään nopeammaksi ja paremmaksi kuin kilpailijasi. 08b426fbee23b7216cf748ab1bde32f1a77927cb 1274 1273 2024-08-22T12:01:54Z Aureon100 6061 wikitext text/x-wiki Johdat imperiumin laajentamista. Sinun täytyy valita polku, joka auttaa sinua kehittymään nopeammaksi ja paremmaksi kuin kilpailijasi. e9efbc5135e3c7fbfad3753fb68d991164879964 1284 1274 2024-08-28T15:31:36Z Aureon100 6061 wikitext text/x-wiki Tarkoituksenasi on johtaa imperiumin laajentamista. Valitse strategia, joka auttaa imperiumiasi kehittymään nopeammin ja tuottavammaksi kuin vastustajasi. 2b6368e589edc1d73d91e5eb86894c4bbb461ff8 1291 1284 2024-09-04T18:17:38Z Aureon100 6061 wikitext text/x-wiki Pelissä tarkoituksenasi on johtaa imperiumin laajentamista. Valitse strategia, joka auttaa imperiumiasi kehittymään nopeammin ja tuottavammaksi kuin vastustajasi oma. f286c882bb3f51500c52311dd92179479421f16f 1293 1291 2024-09-11T09:20:40Z Aureon100 6061 wikitext text/x-wiki Tarkoituksenasi on johtaa imperiumin laajentamista. Valitse strategia, joka auttaa imperiumiasi kehittymään nopeammin ja tuottavammaksi kuin vastustajasi oma. 092b93252b1ab53ca8e47266735c771d18480474 Tips itsawonderfulworld 0 216 1275 2024-08-22T14:44:18Z Aureon100 6061 Ak: Uusi sivu: **Priorisoi tuotantosi kasvu** Tuotanto on kahdella ensimmäisellä kierroksella tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. - **Värit** Harmaa: Materiaali... wikitext text/x-wiki **Priorisoi tuotantosi kasvu** Tuotanto on kahdella ensimmäisellä kierroksella tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. - **Värit** Harmaa: Materiaalit. Johtaa kaikkien viiden tyypin tuotantoon. Ei yleensä kasvata pisteitä (tosin monet rakennukset antavat yhden kenraali- tai rahoittaja-poletin, jotka ovat vähintään yhden pisteen arvoisia). Musta: Energia. Johtaa seikkailukortteihin (sininen). Kortit, jotka tuottavat voimaa, ovat hieman harvinaisempia. Sallii pisteiden keräämisen korteilla, jotka antavat yhden pisteen per musta rakennus. Vihreä: Tiede. Johtaa muiden tyyppien tuotantoon, mutta yleensä melko vähäisessä määrin. Sisältää paljon pisteitä antavia kortteja. Keltainen: Rahoitus. Mahdollistaa useamman rahoituskortin tuotannon. Sisältää paljon pisteitä antavia kortteja. Sininen: Seikkailu. Melkein kaikki kortit antavat runsaasti pisteitä. Muutama harvinainen kortti tuottaa joitakin resursseja. - **Tutkaile pelaajien lukumäärää** Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat paljon tehokkaampia peleissä, joissa on vähemmän pelaajia. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai enemmän. 359f5039e96c5b1a93e60d03fd1cfcb92cbe4f88 1276 1275 2024-08-22T14:45:07Z Aureon100 6061 wikitext text/x-wiki * Priorisoi tuotantosi kasvu Tuotanto on kahdella ensimmäisellä kierroksella tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. - * Värit Harmaa: Materiaalit. Johtaa kaikkien viiden tyypin tuotantoon. Ei yleensä kasvata pisteitä (tosin monet rakennukset antavat yhden kenraali- tai rahoittaja-poletin, jotka ovat vähintään yhden pisteen arvoisia). Musta: Energia. Johtaa seikkailukortteihin (sininen). Kortit, jotka tuottavat voimaa, ovat hieman harvinaisempia. Sallii pisteiden keräämisen korteilla, jotka antavat yhden pisteen per musta rakennus. Vihreä: Tiede. Johtaa muiden tyyppien tuotantoon, mutta yleensä melko vähäisessä määrin. Sisältää paljon pisteitä antavia kortteja. Keltainen: Rahoitus. Mahdollistaa useamman rahoituskortin tuotannon. Sisältää paljon pisteitä antavia kortteja. Sininen: Seikkailu. Melkein kaikki kortit antavat runsaasti pisteitä. Muutama harvinainen kortti tuottaa joitakin resursseja. - * Tutkaile pelaajien lukumäärää Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat paljon tehokkaampia peleissä, joissa on vähemmän pelaajia. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai enemmän. 660c117e67faa06b4887f0f31dd13d0cbfdcab9b 1277 1276 2024-08-22T20:20:57Z Aureon100 6061 Added headlines. wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Tuotanto on kahdella ensimmäisellä kierroksella tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää – paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa: Materiaalit. Johtaa kaikkien viiden tyypin tuotantoon. Ei yleensä kasvata pisteitä (tosin monet rakennukset antavat yhden kenraali- tai rahoittaja-poletin, jotka ovat vähintään yhden pisteen arvoisia). Musta: Energia. Johtaa seikkailukortteihin (sininen). Kortit, jotka tuottavat voimaa, ovat hieman harvinaisempia. Sallii pisteiden keräämisen korteilla, jotka antavat yhden pisteen per musta rakennus. Vihreä: Tiede. Johtaa muiden tyyppien tuotantoon, mutta yleensä melko vähäisessä määrin. Sisältää paljon pisteitä antavia kortteja. Keltainen: Rahoitus. Mahdollistaa useamman rahoituskortin tuotannon. Sisältää paljon pisteitä antavia kortteja. Sininen: Seikkailu. Melkein kaikki kortit antavat runsaasti pisteitä. Muutama harvinainen kortti tuottaa joitakin resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat paljon tehokkaampia peleissä, joissa on vähemmän pelaajia. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai enemmän. 081eaf61ef3bcbd704b5bf7c9039390fa64db939 1278 1277 2024-08-24T11:07:11Z Aureon100 6061 /* Priorisoi tuotantosi kasvu */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == – paljon tärkeämpää kuin pisteiden saaminen. Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa: Materiaalit. Johtaa kaikkien viiden tyypin tuotantoon. Ei yleensä kasvata pisteitä (tosin monet rakennukset antavat yhden kenraali- tai rahoittaja-poletin, jotka ovat vähintään yhden pisteen arvoisia). Musta: Energia. Johtaa seikkailukortteihin (sininen). Kortit, jotka tuottavat voimaa, ovat hieman harvinaisempia. Sallii pisteiden keräämisen korteilla, jotka antavat yhden pisteen per musta rakennus. Vihreä: Tiede. Johtaa muiden tyyppien tuotantoon, mutta yleensä melko vähäisessä määrin. Sisältää paljon pisteitä antavia kortteja. Keltainen: Rahoitus. Mahdollistaa useamman rahoituskortin tuotannon. Sisältää paljon pisteitä antavia kortteja. Sininen: Seikkailu. Melkein kaikki kortit antavat runsaasti pisteitä. Muutama harvinainen kortti tuottaa joitakin resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 47caa691e898ca692d84cb0dd706918f832ddc59 1279 1278 2024-08-24T11:07:54Z Aureon100 6061 /* Priorisoi tuotantosi kasvu */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa: Materiaalit. Johtaa kaikkien viiden tyypin tuotantoon. Ei yleensä kasvata pisteitä (tosin monet rakennukset antavat yhden kenraali- tai rahoittaja-poletin, jotka ovat vähintään yhden pisteen arvoisia). Musta: Energia. Johtaa seikkailukortteihin (sininen). Kortit, jotka tuottavat voimaa, ovat hieman harvinaisempia. Sallii pisteiden keräämisen korteilla, jotka antavat yhden pisteen per musta rakennus. Vihreä: Tiede. Johtaa muiden tyyppien tuotantoon, mutta yleensä melko vähäisessä määrin. Sisältää paljon pisteitä antavia kortteja. Keltainen: Rahoitus. Mahdollistaa useamman rahoituskortin tuotannon. Sisältää paljon pisteitä antavia kortteja. Sininen: Seikkailu. Melkein kaikki kortit antavat runsaasti pisteitä. Muutama harvinainen kortti tuottaa joitakin resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. b43e87003dc5263310afab748561007c77ff624c 1285 1279 2024-08-28T16:28:38Z Aureon100 6061 /* Värit */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittaja-poletteja, joista kumpikin ovat vähintään yhden pisteen arvoinen. rakennus. kortteja. kortteja. Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittaja-poletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa, mutta yleensä vain pienissä määrin. Vihreät kortit tarjoavat paljon pisteitä, mikä tekee niistä arvokkaita loppupelissä. Keltainen (Rahoitus): Tämä resurssi mahdollistaa useampien rahoituskorttien tuotannon ja sisältää kortteja, jotka antavat runsaasti pisteitä. Sininen (Seikkailu): Useimmat siniset kortit antavat runsaasti pisteitä. Joitakin harvinaisia kortteja on myös, jotka tuottavat resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 367af4be164cc6dd7c612dc59bcfb74d84499154 1286 1285 2024-08-28T16:29:38Z Aureon100 6061 /* Värit */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == rakennus. Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittaja-poletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa, mutta yleensä vain pienissä määrin. Vihreät kortit tarjoavat paljon pisteitä, mikä tekee niistä arvokkaita loppupelissä. Keltainen (Rahoitus): Tämä resurssi mahdollistaa useampien rahoituskorttien tuotannon ja sisältää kortteja, jotka antavat runsaasti pisteitä. Sininen (Seikkailu): Useimmat siniset kortit antavat runsaasti pisteitä. Joitakin harvinaisia kortteja on myös, jotka tuottavat resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 85e6cd6af0581547ee43119eb72e65de0490e8fc 1287 1286 2024-08-28T16:30:00Z Aureon100 6061 wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittaja-poletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa, mutta yleensä vain pienissä määrin. Vihreät kortit tarjoavat paljon pisteitä, mikä tekee niistä arvokkaita loppupelissä. Keltainen (Rahoitus): Tämä resurssi mahdollistaa useampien rahoituskorttien tuotannon ja sisältää kortteja, jotka antavat runsaasti pisteitä. Sininen (Seikkailu): Useimmat siniset kortit antavat runsaasti pisteitä. Joitakin harvinaisia kortteja on myös, jotka tuottavat resursseja. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 2e6474a50762fa70aa7776951c559f0fd38e1cab 1288 1287 2024-08-28T16:34:40Z Aureon100 6061 /* Värit */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittaja-poletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa, mutta yleensä vain pienissä määrin. Vihreät kortit tarjoavat paljon pisteitä, mikä tekee niistä arvokkaita loppupelissä. Keltainen (Rahoitus): Rahoitusresurssi auttaa tuottamaan enemmän rahoituskortteja, jotka usein tuovat mukanaan merkittävästi pisteitä. Se on avainasemassa, jos tavoitteena on maksimoida pisteet tehokkaasti. Sininen (Seikkailu): Seikkailukortit ovat tunnettuja korkeasta pistepotentiaalistaan, ja lähes kaikki siniset kortit antavat runsaasti pisteitä. Vaikka siniset kortit harvemmin tuottavat resursseja, ne ovat arvokkaita korkean pistearvonsa ansiosta. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. f010f6161dc1281fee559cd9f4ea8d12a970cb54 1289 1288 2024-08-28T19:07:35Z Aureon100 6061 /* Värit */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tämän resurssin avulla voidaan tuottaa kaikkia viittä resurssityyppiä, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittajapoletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa, mutta yleensä vain pienissä määrin. Vihreät kortit tarjoavat paljon pisteitä, mikä tekee niistä arvokkaita loppupelissä. Keltainen (Rahoitus): Rahoitusresurssi auttaa tuottamaan enemmän rahoituskortteja, jotka usein tuovat mukanaan merkittävästi pisteitä. Se on avainasemassa, jos tavoitteena on maksimoida pisteet tehokkaasti. Sininen (Seikkailu): Seikkailukortit ovat tunnettuja korkeasta pistepotentiaalistaan, ja lähes kaikki siniset kortit antavat runsaasti pisteitä. Vaikka siniset kortit harvemmin tuottavat resursseja, ne ovat arvokkaita korkean pistearvonsa ansiosta. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 94b48d8dfb40e155efead32037045a1e828300f0 1290 1289 2024-08-29T10:47:25Z Aureon100 6061 /* Värit */ wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tätä resurssia käytetään monien eri rakennusten rakentamiseen, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittajapoletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa vain pienissä määrin. Sitä myös käytetään tutkimuslaitosten ja teknologiaprojektien rakentamiseen. Vihreät kortit tarjoavat jonkin verran pisteitä, mikä tekee niistä arvokkaita pelin loppupuoliskolla. Keltainen (Rahoitus): Rahoitusresurssi auttaa tuottamaan enemmän rahoituskortteja, jotka usein tuovat mukanaan merkittävästi pisteitä. Sininen (Seikkailu): Seikkailukortit ovat tunnettuja korkeasta pistepotentiaalistaan, ja lähes kaikki siniset kortit antavat runsaasti pisteitä. Vaikka siniset kortit harvemmin tuottavat resursseja, ne ovat arvokkaita korkean pistearvonsa ansiosta. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. b4d54c8c84bab322ebf5bc78d35500e038290789 1292 1290 2024-09-04T18:29:20Z Aureon100 6061 wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto (kuutioiden kerääminen) on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Tätä resurssia käytetään monien eri rakennusten rakentamiseen, mutta se ei yleensä tuota suoraan pisteitä. Monet rakennukset voivat kuitenkin tuottaa kenraali- tai rahoittajapoletteja, joista kumpikin on vähintään yhden pisteen arvoinen. Musta (Energia): Energia mahdollistaa seikkailukorttien (sininen) tuotannon. Kortit, jotka tuottavat energiaa, ovat hieman harvinaisempia. Mustalla resurssilla voi ansaita pisteitä korttien avulla, jotka antavat yhden pisteen jokaista mustaa rakennusta kohden. Vihreä (Tiede): Tämä resurssi tukee muiden resurssien tuotantoa vain pienissä määrin. Sitä myös käytetään tutkimuslaitosten ja teknologiaprojektien rakentamiseen. Vihreät kortit tarjoavat jonkin verran pisteitä, mikä tekee niistä arvokkaita pelin loppupuoliskolla. Keltainen (Rahoitus): Rahoitusresurssi auttaa tuottamaan enemmän rahoituskortteja, jotka usein tuovat mukanaan merkittävästi pisteitä. Sininen (Seikkailu): Seikkailukortit ovat tunnettuja korkeasta pistepotentiaalistaan, ja lähes kaikki siniset kortit antavat runsaasti pisteitä. Vaikka siniset kortit harvemmin tuottavat resursseja, ne ovat arvokkaita korkean pistearvonsa ansiosta. Niihin suuntautuva strategia vaatii kuitenkin usein vahvaa resurssienhallintaa ja tarkkaa ajoitusta. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 8fedbcf3cdb55e8c43afa7d165997d21f94d1874 1294 1292 2024-09-11T14:21:16Z Aureon100 6061 There was too much unrelevant information. wikitext text/x-wiki == Priorisoi tuotantosi kasvu == Ensimmäisillä kahdella kierroksella tuotanto (kuutioiden kerääminen) on ensiarvoisen tärkeää - paljon tärkeämpää kuin pisteiden saaminen. Tuotannon tyyppi, jota kasvatat, määrittää strategiasi myöhemmillä kierroksilla. == Värit == Harmaa (Materiaalit): Perustuotantoresurssi. Tärkeä aloitusvaiheissa. Vaaditaan monissa projekteissa. Musta (Energia): Tärkeä aloitusvaiheissa. Tukee arvokkaampien kohteiden rakentamista. Vihreä (Tiede): Kerääminen vaatii hyvää tuotannon suunnittelua. Keltainen (Rahoitus): Hankkiminen on vaikeampaa kuin perusresurssien hankinta. Tuotanto on ajoitettava tarkasti hyödyllisiin hetkiin. Sininen (Seikkailu): Tunnettuja suuresta pistepotentiaalistaan. Tarvitaan suurimpien projektien rahoittamiseen. == Tutkaile pelaajien lukumäärää == Mitä vähemmän pelaajia on, sitä enemmän tuotantobonuksia voit saada. Rahoittaja- ja kenraalipoletit ovat tehokkaampia peleissä, joissa on vähemmän pelaajia mukana. Toinen ero on se, että mitä enemmän pelaajia on, sitä tehokkaampia erikoistuvat strategiat ovat. Esimerkiksi vihreä ei yleensä ole paras väri mihin keskittyä, ellei pelaajien lukumäärä ole neljä tai sitä suurempi. 0f9647b8fc7ba3f4dbe85e8415b31a1380870076 Gamehelpmountaingoats 0 217 1280 2024-08-25T10:24:29Z Silkkii 5977 Ak: Uusi sivu: ===Päämäärä=== Kilpailkaa vuohienne kanssa pääsystä vuorten huipulle niin monta kertaa kuin mahdollista, samalla yrittäen kiivetä mahdollisimman monelle eri huipulle. ===Vuoro=== Vuoron aluksi heitetään 4 noppaa. Jos useampi kuin yksi noppa näyttää ykköstä, voit muuttaa ylimääräiset ykköset haluamiksesi numeroiksi, mutta sinun on jätettävä vähintään yksi noppa näyttämään ykköstä. Jaa nopat haluamiisi ryhmiin. Jokainen ryhmä, jonka summa... wikitext text/x-wiki ===Päämäärä=== Kilpailkaa vuohienne kanssa pääsystä vuorten huipulle niin monta kertaa kuin mahdollista, samalla yrittäen kiivetä mahdollisimman monelle eri huipulle. ===Vuoro=== Vuoron aluksi heitetään 4 noppaa. Jos useampi kuin yksi noppa näyttää ykköstä, voit muuttaa ylimääräiset ykköset haluamiksesi numeroiksi, mutta sinun on jätettävä vähintään yksi noppa näyttämään ykköstä. Jaa nopat haluamiisi ryhmiin. Jokainen ryhmä, jonka summa on 5–10, liikuttaa vuohtasi kyseisellä vuoren polulla, mutta ryhmät, joiden summa on tämän ulkopuolella, eivät liikuta vuohtasi. Useat vuohet voivat olla samassa ruudussa, paitsi jos ne ovat vuoren huipulla. Kun pelaaja liikuttaa vuohen vuoren huipulle, hän saa kyseisen huipun pistemerkin (jos niitä on jäljellä) ja jos huipulla oli jo jonkin toisen pelaajan vuohi, se palautetaan takaisin kyseisen vuoren polun alkuun. Jos pelaaja muodostaa nopparyhmän joka osoittaa sen vuoren polulle, jossa hänen vuohensa on jo huipulla, hänen vuohensa ei liiku enempää, mutta hän voi ottaa silti yhden pistemerkin kyseisen huipun merkkivarastosta (jos niitä on jäljellä). Jos keräät pistemerkin jokaiselta vuoren huipulta, saat myös isoimman arvoisen vapaan bonusmerkin. Tämä voidaan tehdä useita kertoja aina, kun keräät uuden setin kaikilta huipuilta. ===Pelin päättyminen=== Kun bonusmerkit loppuvat tai KOLMEN huipun tavalliset pistemerkit loppuvat, käynnissä oleva kierros pelataan loppuun ja peli päättyy. Piste- ja bonusmerkkien summat lasketaan yhteen ja eniten pisteitä kerännyt pelaaja voittaa! Tasatilanteessa se pelaaja voittaa, jolla on eniten vuohia huipuilla! Jos tilanne on edelleen tasan, se pelaaja voittaa, jonka vuohi on korkeimman arvon omaavalla huipulla! === Big Mountain -lisäosa === Big Mountain -lisäosan laudalla vuohet nousevat peräkkäin, ja vain <u>korkeimmalle</u> kiivenneet ansaitsevat pisteitä. Pelaajien täytyy valita, taistelevatko he tavallisista vuorenhuipuista vai ottavatko riskin isolla vuorella, jossa ''ajoitus on ratkaisevaa''. Saadaksesi vuohesi tälle vuorelle, sinun tulee valita summa väliltä 11–24 ja vuohesi siirtyy lukua vastaavalle ison vuoren ruudulle, mutta samalla luovutat vuohesi joltain tavallisen vuoren polulta. Voit kuitenkin pelin aikana siirtää vuohia edestakaisin ison vuoren ja tavallisen vuoren polun välillä. Ruutu, johon siirrät vuohesi isolla vuorella täytyy olla vapaana, eli et voi syrjäyttää toisen pelaajan vuohia tällä laudalla. Pelin lopussa pisteitä annetaan isolla vuorella olevien vuohiesi '''määrän''' ja '''sijoituksen''' perusteella. 2 pelaajan pelissä pisteet ovat 12/8/4; 3 pelaajan pelissä 16/12/8/4; ja 4 pelaajan pelissä 15/12/9/6/3. Pelaajat voivat saada pisteitä <u>useita kertoja</u>, jos heillä on vuohia kelvollisilla paikoilla. Esimerkiksi 2 pelaajan pelin lopussa, jos sinulla on vuohi kohdassa 23 ja toinen kohdassa 16 ja vastustajallasi on vuohi kohdassa 21, saat 16 pistettä (12+4) ja vastustajasi saa 8 pistettä. Tämä lisäosa antaa sinulle enemmän vaihtoehtoja, sillä voit nyt siirtää vuohia isolle vuorelle silloin, kun tavallisten vuorten huipuilta ei enää ole saatavilla pistemerkkejä. 1eb3d5f29632ae947581805bdc1e7e963795fe16 1283 1280 2024-08-25T13:56:31Z Silkkii 5977 wikitext text/x-wiki ===Päämäärä=== Pelaajat kipuavat vuohillaan kilpaa vuoren huipulle, mahdollisesti montakin kertaa ja samalla yrittäen kiivetä mahdollisimman monelle eri huipulle. ===Vuoro=== Vuoron aluksi heitetään 4 noppaa. Jos useampi kuin yksi noppa näyttää ykköstä, voit muuttaa ylimääräiset ykköset haluamiksesi numeroiksi, mutta sinun on jätettävä vähintään yksi noppa näyttämään ykköstä. Jaa nopat haluamiisi ryhmiin. Jokainen ryhmä, jonka summa on 5–10, liikuttaa vuohtasi kyseisellä vuoren polulla, mutta ryhmät, joiden summa on tämän ulkopuolella, eivät liikuta vuohtasi. Useat vuohet voivat olla samassa ruudussa, paitsi jos ne ovat vuoren huipulla. Kun pelaaja liikuttaa vuohen vuoren huipulle, hän saa kyseisen huipun pistemerkin (jos niitä on jäljellä) ja jos huipulla oli jo jonkin toisen pelaajan vuohi, se palautetaan takaisin kyseisen vuoren polun alkuun. Jos pelaaja muodostaa nopparyhmän joka osoittaa sen vuoren polulle, jossa hänen vuohensa on jo huipulla, hänen vuohensa ei liiku enempää, mutta hän voi ottaa silti yhden pistemerkin kyseisen huipun merkkivarastosta (jos niitä on jäljellä). Jos keräät pistemerkin jokaiselta vuoren huipulta, saat myös isoimman arvoisen vapaan bonusmerkin. Tämä voidaan tehdä useita kertoja aina, kun keräät uuden setin kaikilta huipuilta. ===Pelin päättyminen=== Kun bonusmerkit loppuvat tai KOLMEN huipun tavalliset pistemerkit loppuvat, käynnissä oleva kierros pelataan loppuun ja peli päättyy. Piste- ja bonusmerkkien summat lasketaan yhteen ja eniten pisteitä kerännyt pelaaja voittaa! Tasatilanteessa se pelaaja voittaa, jolla on eniten vuohia huipuilla! Jos tilanne on edelleen tasan, se pelaaja voittaa, jonka vuohi on korkeimman arvon omaavalla huipulla! === Big Mountain -lisäosa === Big Mountain -lisäosan laudalla vuohet nousevat peräkkäin, ja vain <u>korkeimmalle</u> kiivenneet ansaitsevat pisteitä. Pelaajien täytyy valita, taistelevatko he tavallisista vuorenhuipuista vai ottavatko riskin isolla vuorella, jossa ''ajoitus on ratkaisevaa''. Saadaksesi vuohesi tälle vuorelle, sinun tulee valita summa väliltä 11–24 ja vuohesi siirtyy lukua vastaavalle ison vuoren ruudulle, mutta samalla luovutat vuohesi joltain tavallisen vuoren polulta. Voit kuitenkin pelin aikana siirtää vuohia edestakaisin ison vuoren ja tavallisen vuoren polun välillä. Ruutu, johon siirrät vuohesi isolla vuorella täytyy olla vapaana, eli et voi syrjäyttää toisen pelaajan vuohia tällä laudalla. Pelin lopussa pisteitä annetaan isolla vuorella olevien vuohiesi '''määrän''' ja '''sijoituksen''' perusteella. 2 pelaajan pelissä pisteet ovat 12/8/4; 3 pelaajan pelissä 16/12/8/4; ja 4 pelaajan pelissä 15/12/9/6/3. Pelaajat voivat saada pisteitä <u>useita kertoja</u>, jos heillä on vuohia kelvollisilla paikoilla. Esimerkiksi 2 pelaajan pelin lopussa, jos sinulla on vuohi kohdassa 23 ja toinen kohdassa 16 ja vastustajallasi on vuohi kohdassa 21, saat 16 pistettä (12+4) ja vastustajasi saa 8 pistettä. Tämä lisäosa antaa sinulle enemmän vaihtoehtoja, sillä voit nyt siirtää vuohia isolle vuorelle silloin, kun tavallisten vuorten huipuilta ei enää ole saatavilla pistemerkkejä. 6b03f5183719b8e7d1ba8dc2ecd4112c6b428c05 Gamehelpcubirds 0 218 1281 2024-08-25T13:49:06Z Silkkii 5977 Ak: Uusi sivu: [[Category:Card games]] __NOTOC__ Pelin tavoitteena on kerätä kokoelma lintuja 'lennättämällä kotiin' käsikorttien parvia. Pelaaja voittaa, kun hänen kokoelmansa sisältää joko: - Ainakin yhden linnun seitsemästä eri lajista - Kolme lintua kahdesta eri lajista Pelin alussa pöydälle jaetaan 4 korttiriviä, joissa kussakin on 4 esiin käännettyä lintukorttia ja lisäksi jokainen pelaaja saa yhden satunnaisen linnun omaan kokoelmaansa. Jokainen pelaaja aloitta... wikitext text/x-wiki [[Category:Card games]] __NOTOC__ Pelin tavoitteena on kerätä kokoelma lintuja 'lennättämällä kotiin' käsikorttien parvia. Pelaaja voittaa, kun hänen kokoelmansa sisältää joko: - Ainakin yhden linnun seitsemästä eri lajista - Kolme lintua kahdesta eri lajista Pelin alussa pöydälle jaetaan 4 korttiriviä, joissa kussakin on 4 esiin käännettyä lintukorttia ja lisäksi jokainen pelaaja saa yhden satunnaisen linnun omaan kokoelmaansa. Jokainen pelaaja aloittaa kahdeksalla käsikortilla. Peli alkaa jakajasta ja jatkuu myötäpäivään. Peliä pelataan useissa kierroksissa. Jokaisen kierroksen alussa pelaajat heittävät kätensä pois ja nostavat 8 korttia pakasta. Henkilö, joka päätti edellisen kierroksen, aloittaa ensimmäisenä. Kierros jatkuu, kunnes joku voittaa pelin tai pelaajalla ei ole kortteja kädessään. ==Vuoro== ===Aseta lintuja=== Pelaajan on vuorollansa asetettava yksi tai useampi lintu kädestään. '''Kaikki valitun lajin linnut''', jotka pelaajalla on kädessään, on asetettava samaan aikaan. Pelaaja asettaa nämä linnut (toistensa viereen) jonkin pöydän keskellä olevan neljän rivin jomman kumman reunan vasemmalle tai oikealle puolelle. Kun pelaaja asettaa linnun riviin, jossa on jo yksi tai useampi saman lajin lintu, pelaajan on otettava kaikki näiden väliin jäävät kortit käteensä. Esimerkiksi, jos pelaaja lisäisi kaksi Papukaijaa tämän rivin vasemmalle puolelle, hän ottaisi Pöllön ja Flamingon: [[File: Cubirds illustration.png]] Kun ympäröivät kortit on otettu, jäljellä olevat linnut siirretään yhteen ja pakasta lisätään tälle riville kortteja (jotka asetetaan rivin vasempaan tai oikeaan laitaan), kunnes siinä on kahta eri lajia. Jos pöydältä ei nostettu yhtään korttia, pelaaja voi halutessaan nostaa pakasta kaksi korttia. ===Täydennä parvi tai "Lennätä kotiin" (valinnainen)=== Pelaaja voi kääntää käsikorteistaan esille saman lajin lintuparven ja yksi tai kaksi näistä korteista lisätään hänen kokoelmaansa. Jokaisessa kortissa on kaksi numeroa, joista ensimmäinen osoittaa pieneen parveen vaaditun määrän ja jälkimmäinen isoon parveen vaaditun määrän. Jos esiin käännettyjen korttien määrä on yhtä suuri tai suurempi kuin: - '''Pieni parvi''': pelaaja ''lisää yhden'' esiin käännetyn kortin kokoelmaansa; - '''Iso parvi''': pelaaja ''lisää kaksi'' esiin käännettyä korttia kokoelmaansa. Jäljelle jäävät kortit laitetaan poistopakkaan. Pelaaja voi vuoronsa aikana täydentää vain yhden parven (pienen tai ison). ''(Huomaa, että BGA:n toteutuksessa annetaan muutama sekunti "ajatteluaikaa" tälle päätökselle, vaikka et voisi pelata parvea, jotta vastustajasi ei voi päätellä, onko sinulla kädessäsi parvi, jota ei ole vielä otettu.)'' ==Kierroksen päättyminen== Kierros päättyy, kun jollain pelaajalla ei ole enää kortteja kädessään. Muiden pelaajien käsikortit laitetaan poistopakkaan. Pöydän keskellä riveissä olevat kortit jäävät paikoilleen. Pelaajasta, joka päätti kierroksen, tulee uusi jakaja ja hän aloittaa uuden kierroksen. ==Pelin päättyminen== Pelaaja voittaa välittömästi, jos hän kerää: - 7 eri lajia, tai - 2 eri lajia, joista kummastakin vähintään 3 kokoelmaa Jos on mahdotonta jakaa 8 korttia jokaiselle pelaajalle (jopa poistopakan avulla), peli päättyy välittömästi. Tässä tapauksessa pelaaja, jolla on eniten lintukortteja kokoelmassaan, voittaa pelin. ==Erilaisten lintujen parvikoot== * Flamingo: pieni 2, suuri 3, lukumäärä 7 * Pöllö/Tukaani: pieni 3, suuri 4, lukumäärä 10 * Sorsa/Papukaija: pieni 4, suuri 6, lukumäärä 13 * Harakka: pieni 5, suuri 7, lukumäärä 17 * Rytikerttunen/Punarinta: pieni 6, suuri 9, lukumäärä 20 d07561b45d52e656d4046773ee14bb4d94eb3dd9 Tiedosto:Cubirds illustration.png 6 219 1282 2024-08-25T13:49:29Z Silkkii 5977 wikitext text/x-wiki da39a3ee5e6b4b0d3255bfef95601890afd80709 Gamehelpdaybreak 0 220 1295 2024-11-09T20:00:58Z Rexroom 5688 Ensimmäinen versio wikitext text/x-wiki Yhteistyöpeli, jossa yritetään kääntää maailman kasvihuonepäästöjen tuotanto neutraaliksi ennen kuin tilanne riistäytyy käsistä. == Alkuasettelu == == Pelin kulku == Peliä pelataan korkeintaan 6 kierrosta. Jokainen kierros menee vaiheittain seuraavasti: '''Globaalivaihe''' paljastaa tulevat kriisikortit, jotka vaikeuttavat pelaajien toimia, ja pelaajien globaalihankkeita, joista otetaan yksi käyttöön. '''Paikallisvaihe''' antaa pelaajien rakentaa ja reagoida uhkiin. Tämän vaiheen aikana pelaajat pelaavat korttejaan ja auttavat toisiaan. '''Päästövaihe''' lisää ilmakehään pelaajien tuottamat päästöt. '''Kriisivaihe''' hoidetaan kahdessa osassa: 1) ilmastonoppa 2) kriisikortit. '''Kasvuvaihe''' pelaajien energiavaatimuksia kasvatetaan. === Globaalivaihe === Nostetaan kriisikortteja, joista yksi paljastetaan. Ensimmäisellä kierroksella nostetaan kolme, myöhemmissä kierroksissa nostetaan maailman lämpötilan mukaan. Sitten nostetaan kaksi globaalihankekorttia, josta pelaajat valitsevat yhden. '''Vinkki:''' Valitkaa globaalihankkeista se, josta on eniten hyötyä. === Paikallisvaihe === Pelaajat voivat aktivoida rivistään kortteja ja pelata korttejaan kädestään. Huomioi korttien ikoneita: näillä voi täyttää tai vahvistaa joidenkin korttien ehtoja. === Päästövaihe === Katsotaan, onnistuvatko pelaajat vastaamaan lautojensa energiakulutustaan. Jos ei, he saavat vajauksen verran kriisiyhteisöjä. Sitten lisätään ilmakehään päästökuutioita jokaista päästömerkkiä ja likaista energiatuotantoa kohden. Näistä kuutioista osa menee puihin, mereen ja DAC-koneisiin. Jos tämän jälkeen ei ole kuutioita, pelaajat ovat päässeet hiilineutraaliuteen! Muuten jäljellä olevat kuutiot sijoitetaan lämpömittariin. === Kriisivaihe === Heitetään ilmastovaikutusnoppaa jokaista desimaaliastetta kohden. Kriisikortteja käsitellään yksitellen. === Kasvuvaihe === Jos saavutitte hiilineutraaliuden, voitatte pelin! Muuten kasvata lautojenne energiakulutusta. == Pelin loppu == Pelaajat voittavat kun he saavuttavat hiilineutraaliuden. Pelaajat häviävät, jos lämpömittarissa on 8 lämpönauhaa, jollain pelaajalla on 12 kriisiyhteisöä tai ette ole voittaneet 6. kierroksen lopussa. 1c883fdc897b2bdee31971e8af20d2f8063805cf Gamehelpgomoku 0 81 1296 422 2025-01-06T08:12:26Z Rexroom 5688 Korvataan suomennoksella wikitext text/x-wiki Kahden pelaajan abstrakti strategialautapeli, jossa pyritään rakentamaan laudalle viiden kiven suora joko pystysuoraan, vaakasuoraan tai vinottain. == Pelin kulku == Pelaajat pelaavat asettelemalla vuorotellen Go-laudalle oman värinsä kiviä tyhjiin risteyksiin. Pelaaja voittaa, kun hän saa rakennettua tasan viiden kiven suoran joko pystysuoraan, vaakasuoraan tai diagonaalisesti. Suoran on oltava katkeamaton, eikä yli viiden kiven suoria huomioida. == Avaussäännöt == Musta aloittaa pelin asettamalla kivensä laudalle. === Pro ja Pitkä pro === Musta asettaa kivensä laudan keskelle. Valkoinen asettaa omansa neljän/viiden risteyksen päästä mustasta. === Vaihto === Musta asettaa laudalle kolme kiveä, eli kaksi mustaa ja yhden valkoisen, minne tahansa laudalle. Valkoinen voi sitten vaihtaa värinsä mustan kanssa, siten vaihtaen puolia. Tämän jälkeen valkoinen jatkaa peliä normaalisti. === Vaihto2 === Musta asettaa laudalle kolme kiveä, eli kaksi mustaa ja yhden valkoisen, minne tahansa laudalle. Tämän jälkeen valkoinen voi joko (1) vaihtaa värinsä mustan kanssa, siten vaihtaen puolia, (2) jatkaa valkoisena, tai (3) asettaa laudalle kaksi kiveä, yhden mustan ja yhden valkoisen, ja antaa mustalle mahdollisuuden vaihtaa värejä. == Muunnelmat == === Vapaamuotoinen === Pelaaja voittaa, kun hän saa rakennettua vähintään viiden kiven katkeamattoman suoran joko pystysuoraan, vaakasuoraan tai diagonaalisesti. === Caro === Pelaaja voittaa, kun hän saa rakennettua vähintään viiden kiven katkeamattoman suoran joko pystysuoraan, vaakasuoraan tai diagonaalisesti, ja jonka päissä ei ole vastustajan kiveä. Pelaaja voi myös voittaa kuuden tai useamman kiven suoralla, jolloin vastustajan kiveä ei huomioida. 4301bbe9e2aa1dfc9e6c69db47c4648237523527